型キャスト(Type Casting) - Golang learning step 1-6
- 公開日
- カテゴリ:LearnTheBasics
- タグ:Golang,roadmap.sh,学習メモ
roadmap.sh > Go > Learn the Basics > Type Casting の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
型キャスト(Type Casting)
型キャスト(型変換)は、データ型を他の型に変換するためのメカニズムです。型キャストを使うことで、あるデータ型を明示的に別の型に変換することができます。
ただし、Go には暗黙の型変換はありません。型キャストを行う場合は、明示的に変換を指定する必要があります。
- [official] Tour of Go: Type Casting Basics
- [article] Go Docs: Type Casting
型キャストの基本構文
Go では、型キャストは次のような構文で行います。
newType(value)
newType
は変換先の型を指定し、value
は変換する値です。
int 型から float64 型への変換
int 型の i を float64 型にキャストしてみます。
package main
import "fmt"
func main() {
var i int = 42
var f float64 = float64(i)
fmt.Printf("int: %d, float64: %f\n", i, f)
// => int: 42, float64: 42.000000
}
i は int なので value が 42 ですが、f は float64 にキャストされたため value が 42.000000 となっていることがわかります。
型キャストと型アサーション
Go では、型キャストと型アサーションを区別します。型キャストはプリミティブ型(基本的なデータ型)間の変換に使われますが、インターフェース型から特定の型への変換には「型アサーション」を使います。
型アサーションの例
package main
import "fmt"
func main() {
var i interface{} = "Hello"
// interface{} 型の変数 i に格納されている値が string 型であるかを確認。string 型であれば s に値を返す
s, ok := i.(string)
if ok {
fmt.Println("String value:", s)
} else {
fmt.Println("Value is not a string")
}
}
この例では、interface{} 型の変数 i から string 型へのアサーションを行っています。型アサーションに成功した場合は、ok が true になり、失敗した場合は ok が false になります。ok が true の場合、i を string として扱い、成功すればその値を取得(s に "Hello" が返る)します。
Go では、インターフェース型は異なる型の値を格納できますが、その具体的な型は実行時まで分かりません。そのため、インターフェース型に格納された値が特定の型であることを確認したり、実際にその型として値を取り出したりするために、型アサーションが使われます。
型アサーションの誤用とエラー処理
型アサーションは誤用すると、パニック(ランタイムエラー)を引き起こす可能性があります。型アサーションを使用する際に、ok を省略して次のように書くこともできます。
var i interface{} = 42
s := i.(string) // panic: interface conversion: interface {} is int, not string
しかし、この場合、i が string でない場合はパニックが発生します。安全に使うためには、ok を使った形式が推奨されています。
型アサーションはインターフェース型以外には使えない?
基本的に、型アサーションはインターフェース型に対してのみ適用可能です。インターフェース型でない型に対しては、直接の型キャスト(Type(value))を使用します。
型アサーションが使用される場面
- インターフェース型から具体的な型を取り出すとき。
- インターフェース型がどの具体的な型を保持しているかを確認するとき。
- インターフェースを実装する異なる型を扱うとき。
型キャストが失敗するケース
型キャストは常に実行されますが、不適切な型キャストを行った場合、キャスト後の値が予期しない結果になることがあります。これを「失敗」と解釈できるケースがありますが、Goのコンパイラ自体はこれをエラーとして扱いません。
例えば、非常に大きな整数を float32 にキャストした場合、浮動小数点数の精度の限界によって値が変わることがあります。
var i int = 12345678910111213
var f float32 = float32(i)
fmt.Printf("int: %d, float32: %f\n", i, f)
// int: 12345678910111213, float32: 12345678407663616.000000
float32 の有効数字は約7桁のため、それより大きな整数をキャストすると、精度が低く丸め誤差が発生していることがわかります。
また、文字列から数値やその逆は、直接キャストできません。例えば、次のコードはコンパイルエラーになります。
var s string = "123"
var i int = int(s) // エラー: cannot convert s (variable of type string) to type int
文字列と数値の変換
文字列を数値に変換したい場合は、strconv パッケージを利用します。
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123"
i, err := strconv.Atoi(s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(i)
}
}
strconv.Atoi は string を int に変換しますが、変換が失敗した場合にはエラーを返します。
逆に、数値を文字列に変換する場合は strconv.Itoa を使います。
package main
import (
"fmt"
"strconv"
)
func main() {
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
}
文字列とバイトの変換
Go 言語では、文字列とバイトスライスを相互に変換することがよくあります。Go の文字列は UTF-8 エンコードされたバイトのシーケンスです。一方、バイトスライスは []byte 型で、文字列の各文字をバイトとして格納するデータ型です。文字列とバイトスライスの変換は簡単に行えますが、それぞれの特徴を理解することが重要です。
文字列からバイトスライスへの変換
文字列はイミュータブル(変更不可)なデータ型ですが、バイトスライスは変更可能です。文字列をバイトスライスに変換すると、その文字列の各バイトがスライスとして表現されます。
以下のコードでは、"Hello, Go!" という文字列をバイトスライスに変換しています。
str := "Hello, Go!"
bytes := []byte(str)
fmt.Println(bytes)
// => [72 101 108 108 111 44 32 71 111 33]
出力された数字の羅列は、文字列 "Hello, Go!" の各文字がバイト列に変換されたものです。
バイトスライスから文字列への変換
逆に、バイトスライスを文字列に変換することもできます。バイトスライスが UTF-8 エンコードされたバイトであれば、正しい文字列として扱えます。
以下のコードでは、バイトスライス [72 101 108 108 111 44 32 71 111 33] を文字列に変換しています。出力結果が Hello, Go! になります。
bytes := []byte{72, 101, 108, 108, 111, 44, 32, 71, 111, 33}
str := string(bytes)
fmt.Println(str)
// => Hello, Go!
このように、バイトスライスをそのまま string 型に変換することで、文字列を生成できます。
文字列とバイトスライスの違い
- 文字列 (string) はイミュータブルなデータ型で、変更することができません。
- バイトスライス ([]byte) はミュータブルなデータ型で、個々のバイトを変更できます。
例えば、バイトスライスの一部を変更したい場合、次のように操作します。
bytes := []byte("Hello, Go!")
bytes[7] = 'g'
fmt.Println(string(bytes))
// => Hello, go!
この例では、バイトスライスのインデックス7の位置にある文字を 'G' から 'g' に変更しています。結果として、文字列が "Hello, go!" になります。
日本語などのマルチバイト文字の扱い
UTF-8 では、日本語やその他のマルチバイト文字は 1 文字が複数のバイトで表現されることがあります。したがって、文字列をバイトスライスに変換した場合、期待する長さのバイトスライスにならないことがあります。
str := "こんにちは"
bytes := []byte(str)
fmt.Println(bytes)
// 各バイトのコードポイントが表示される
// => [227 129 147 227 130 147 227 129 171 227 129 161 227 129 175]
fmt.Println(len(str))
// 文字列の長さ(文字数): 5
// => 15
fmt.Println(len(bytes))
// バイトスライスの長さ(バイト数): 15
// => 15
"こんにちは" という文字列は 5 文字ですが、UTF-8 では各文字が 3 バイトでエンコードされるため、バイトスライスに変換すると 15 バイトになります。つまり、1 文字が複数バイトで表現されるため、文字列の長さとバイト数が一致しないことがある点に注意が必要です。
除算中の型変換
Go 言語における除算中の型変換には注意が必要です。特に、整数型での除算と浮動小数点型での除算は結果が異なります。
整数同士での除算は整数除算が行われ、小数部分が切り捨てられますが、浮動小数点数を含む場合は浮動小数点除算が行われ、より精密な結果が得られます。
整数型での除算
整数同士で除算を行うと、Go では結果が整数になります。小数点以下は切り捨てられ、結果は整数型として返されます。
例:整数同士の除算
a := 5
b := 2
result := a / b
fmt.Println(result)
// => 2
この場合、5 / 2 は計算上は 2.5 ですが、Go では整数除算が行われ、小数点以下が切り捨てられて 2 という結果が返されます。
浮動小数点型での除算
浮動小数点型(float32 や float64)を使うと、より精密な除算が可能です。この場合、小数点以下も含めて結果が返されます。
例:浮動小数点数での除算
a := 5.0
b := 2.0
result := a / b
fmt.Println(result)
// => 2.5
この例では、5.0 / 2.0 で 2.5 という浮動小数点の結果が返されます。
型変換を使った整数型の除算で小数を得る
整数型の変数を使って正確な除算結果(小数点を含む結果)を得たい場合、型キャストを使って整数を浮動小数点数に変換してから除算を行う必要があります。型キャストを行わないと、整数除算が行われ、小数点以下が切り捨てられます。
例:整数を浮動小数点型にキャストして除算
a := 5
b := 2
result := float64(a) / float64(b)
fmt.Println(result)
// => 2.5
この例では、a と b をそれぞれ float64 に型変換してから除算を行っているため、期待通りの小数点を含む結果 2.5 が得られます。
複合的な型の演算
複数の異なる型(例えば、整数型と浮動小数点型)を一緒に使った演算では、Go では暗黙的な型変換が行われません。つまり、異なる型の演算を行う場合、明示的に型を合わせる必要があります。
例:異なる型の演算
a := 5 // int
b := 2.0 // float64
result := float64(a) / b // aをfloat64にキャスト
fmt.Println(result)
// => 2.5
この例では、a は int 型ですが、b が float64 型であるため、a を float64 にキャストしてから除算を行っています。これにより、正しい小数点を含む結果が得られます。
まとめ
- Go では明示的な型キャストが必要で、newType(value) の形式で行う。
- 型アサーションはインターフェース型から特定の型への変換に使用する。
- 文字列と数値の変換には strconv パッケージを使用する。
- 文字列とバイトスライスの相互変換は []byte(文字列) や string(バイトスライス) で簡単に行える。
- 整数同士の除算では、商が整数となり、小数部分は切り捨てられる。浮動小数点数を使用することで小数点以下を含む結果が得られる。
- 整数での除算で小数点以下を得たい場合、浮動小数点型に型キャストする必要がある。
[Next] Step 1-7: パッケージ(Packages)