Go 型変換
型変換(Type Conversion)とは、あるデータ型の値を別のデータ型として扱うための処理です。
Go言語において、型変換は完全に明示的(Explicit)に行う必要があります。これは、コンパイラに対して「この値をA型からB型に変換する」と明確に伝えなければならないことを意味します。JavaScriptやC言語などで見られる「暗黙的な型変換(コンパイラが自動的に変換する処理)」とは対照的な設計となっています。
1. 型変換を理解する
Goにおける型変換の本質は、既存の値を基にして、異なる型を持つ新しい値を作成することです。その基本構文は非常にシンプルです。
Type(value)ここで、Type は変換後のターゲットとなる型であり、value は変換したい値またはエクスプレッションです。
注意点: Goではあらゆる型を自由に変換できるわけではありません。データの整合性を保証し、無意味な操作を防ぐために、厳格な変換ルールが設けられています。
1.1 許可されている変換
2つの型の低レイヤーなデータ構造に互換性があり、変換の意図が明確である場合、Goは変換を許可します。
- 数値型間: 異なる整数型間(例:
intからint64)、異なる浮動小数点型間(例:float32からfloat64)、および整数と浮動小数点数間の相互変換は許可されています。ただし注意点として、大きな整数から小さな整数への変換はデータの切り捨て(オーバーフロー)を招き、浮動小数点数から整数への変換は小数点以下がすべて失われます。 - 文字列と Byte/Rune スライス: 文字列(string)からバイトスライス(
[]byte)やルーンスライス([]rune)へ、またその逆への変換は非常にスムーズに行えます。これは、低レイヤーのデータ処理やマルチバイト文字(日本語など)を扱う際に非常に有用です。 - 文字列と数値(strconv パッケージが必須):
Type(value)の構文を使って、intからstringへ直接変換することはできません(例:string(123)は"123"にはならず、全く別の文字として扱われます)。この場合は、Go標準のstrconvパッケージに含まれるstrconv.Atoi(文字列から整数)やstrconv.Itoa(整数から文字列)といった関数を使用する必要があります。
1.2 禁止されている変換
Goは、挙動が未定義であったり、データ破壊の恐れがある変換を厳格に禁止しています。
- ブール値と数値/文字列:
true/falseを数値の1/0や文字列の"true"/"false"に直接変換することはできません。その逆も同様です。 - 構造体(Struct)間: たとえ2つの構造体が全く同じフィールドを持っていたとしても、型名が異なる場合は直接変換することはできません。
- 互換性のないスライス:
[]intを[]stringに直接変換することは不可能です。
2. 数値型間の変換
数値型同士の相互変換は日常的な処理ですが、その詳細について見ていきましょう。
2.1 整数から整数への変換
整数の変換において最も注意すべきは、ターゲットとなる型のキャパシティ(容量)が十分であるかどうかです。
package main
import "fmt"
func main() {
var i int32 = 1000
var j int64 = int64(i) // int32 から int64 へ。小さい型から大きい型への安全な変換
fmt.Println(j)
var k int8 = int8(i) // int32 から int8 へ。大きい型から小さい型へ。データ消失のリスクあり
fmt.Println(k) // 出力: -24 (1000 は int8 の最大値 127 を超えているため、オーバーフローが発生)
}2.2 浮動小数点数から整数への変換
浮動小数点数を整数に変換する場合、Goは最も単純な戦略を採用します。すなわち、小数点以下をすべて切り捨てます(床関数/切り捨て)。
package main
import "fmt"
func main() {
var f float64 = 3.14
var i int = int(f) // float64 から int へ
fmt.Println(i) // 出力: 3 (小数点以下は切り捨てられます)
}2.3 整数から浮動小数点数への変換
整数から浮動小数点数への変換は通常安全ですが、整数が極端に大きい場合、精度がわずかに失われる可能性があります。
package main
import "fmt"
func main() {
var i int = 10000000000000001 // 非常に大きな整数
var f float64 = float64(i) // int から float64 へ
fmt.Println(f) // 出力: 1e+16 (float64 の精度の限界により、末尾の '1' が失われる可能性があります)
}3. 文字列(String)に関連する変換
文字列の変換は少し特殊です。低レイヤーのバイト/文字変換と、強力な strconv パッケージを用いた数値との相互変換について解説します。
3.1 文字列から Byte/Rune スライスへの変換
[]byte: 文字列をロウ(生)のバイトシーケンスに分解します(ネットワーク転送やファイル書き込みによく使われます)。[]rune: 文字列を Unicode コードポイントのシーケンスに分解します(マルチバイト文字を正しく処理するために使われます)。
package main
import "fmt"
func main() {
str := "Hello, 世界"
// 文字列からバイトスライスへの変換
byteSlice := []byte(str)
fmt.Printf("%v\n", byteSlice) // 出力: [72 101 108 108 111 44 32 228 184 150 231 149 140] (日本語文字が複数のバイトで構成されている点に注目)
// 文字列からルーンスライスへの変換 (Rune)
runeSlice := []rune(str)
fmt.Printf("%v\n", runeSlice) // 出力: [72 101 108 108 111 44 32 19990 30028] (日本語文字が独立したコードポイントとして正しく認識される)
}3.2 Byte/Rune スライスから文字列への変換
package main
import "fmt"
func main() {
byteSlice := []byte{72, 101, 108, 108, 111}
str1 := string(byteSlice)
fmt.Println(str1) // 出力: Hello
runeSlice := []rune{72, 101, 108, 108, 111, 32, 19990, 30028}
str2 := string(runeSlice)
fmt.Println(str2) // 出力: Hello 世界
}3.3 文字列から数値への変換 (strconv を使用)
"123" という文字列を実際の数値 123 に変換するには、strconv パッケージを使用する必要があります。
strconv.Atoi(ASCII to Integer): 文字列から int へ変換。strconv.ParseFloat: 文字列から浮動小数点数へ変換。
極めて重要: この種の変換は失敗する可能性があるため(例:"abc" を数値に変換しようとする場合)、必ず関数が返すエラー(err)を処理しなければなりません。
package main
import (
"fmt"
"strconv"
)
func main() {
strInt := "123"
intVal, err := strconv.Atoi(strInt)
if err != nil {
fmt.Println("文字列から整数への変換に失敗しました:", err)
return
}
fmt.Println(intVal) // 出力: 123
strFloat := "3.14"
floatVal, err := strconv.ParseFloat(strFloat, 64) // 64 は float64 を意味します
if err != nil {
fmt.Println("文字列から浮動小数点数への変換に失敗しました:", err)
return
}
fmt.Println(floatVal) // 出力: 3.14
}3.4 数値から文字列への変換 (strconv を使用)
strconv.Itoa(Integer to ASCII):intから文字列へ変換。strconv.FormatFloat: 浮動小数点数から文字列へ変換(フォーマットの高度なカスタマイズが可能)。
package main
import (
"fmt"
"strconv"
)
func main() {
intVal := 42
strInt := strconv.Itoa(intVal)
fmt.Println(strInt) // 出力: "42"
floatVal := 2.71828
// 引数説明: 'f' は標準的な小数形式、5 は小数点以下5桁、64 は元のデータが float64 であることを示す
strFloat := strconv.FormatFloat(floatVal, 'f', 5, 64)
fmt.Println(strFloat) // 出力: "2.71828"
}4. 実戦デモンストレーション
実際の開発シーンにおける型変換の応用例を見てみましょう。
4.1 整数のスコアから平均値を計算する
整数のスコアの集合から、小数点を含む正確な平均値を算出したい場合、除算を行う前に浮動小数点数に変換する必要があります。
package main
import "fmt"
func main() {
scores := []int{85, 92, 78, 95, 88}
sum := 0
for _, score := range scores {
sum += score
}
// 重要: float64() によるキャストを行わないと、整数同士の除算となり、小数点以下が切り捨てられます
average := float64(sum) / float64(len(scores))
fmt.Printf("平均スコア: %.2f\n", average) // 出力: 平均スコア: 87.60
}4.2 ユーザーのターミナル入力を処理する
コマンドラインから読み込まれるデータはすべて文字列です。計算に使用したい場合は、まず変換が必要です。
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("数字を入力してください: ")
// 改行までユーザー入力を読み込む
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input) // 前後の空白や改行を削除
// 文字列から整数への変換
num, err := strconv.Atoi(input)
if err != nil {
fmt.Println("無効な入力です。数字ではありません:", err)
return
}
fmt.Printf("入力された数字は: %d です\n", num)
}4.3 金額のフォーマット出力
価格を表示する際、計算済みの浮動小数点数を、UI表示用に小数点以下2桁の文字列にフォーマットすることがよくあります。
package main
import (
"fmt"
"strconv"
)
func main() {
price := 49.99
formattedPrice := strconv.FormatFloat(price, 'f', 2, 64)
fmt.Printf("商品価格: $%s\n", formattedPrice) // 出力: 商品価格: $49.99
}