1. Home
  2. Golang
  3. GuideToBecomingGoDeveloper
  4. LearnTheBasics
  5. 型推論(Type Inference) - Golang learning step 1-5

型推論(Type Inference) - Golang learning step 1-5

  • 公開日
  • カテゴリ:LearnTheBasics
  • タグ:Golang,roadmap.sh,学習メモ
型推論(Type Inference) - Golang learning step 1-5

roadmap.sh > Go > Learn the Basics > Type Inference の学習を進めていきます。

※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。

contents

  1. 開発環境
  2. 型推論(Type Inference)
  3. 短い宣言
  4. 型推論の基本例
  5. 配列やスライスでの型推論
  6. 関数の戻り値による型推論
  7. 定数と型推論
  8. マップ(map)の型推論
  9. 構造体(struct)の型推論
  10. インターフェースの型推論

開発環境

  • チップ: Apple M2 Pro
  • OS: macOS Sonoma
  • go version: go1.23.2 darwin/arm64

型推論(Type Inference)

型推論とは、変数を宣言するときに型を明示せず、コンパイラが文脈から自動的に型を推測する機能です。Go は静的型付けの言語ですが、型推論を使うことで明示的な型指定を省略でき、コードの簡潔さを向上させます。

初期値を持つ変数を宣言すると、Golang は右側の値から変数の型を自動的に推測します。そのため、宣言時に変数を初期化するときに型を指定する必要はありません。

// 型を明示する
var a string = "A"
fmt.Printf("Variable 'a' is of type %T\n", a)
// => Variable 'a' is of type string

// 型推論
var b = "B" // 型を明示しない
fmt.Printf("Variable 'b' is of type %T\n", b)
// => Variable 'b' is of type string

後者の例では、右側の値 "B" から変数の型を自動的に推測(=string)します。

変数を他の型の値に再割り当てしようとすると、コンパイラはエラーをスローします。

var b = "B"

b = 1 // => cannot use 1 (untyped int constant) as string value in assignment

短い宣言

Go では、:= 演算子を使用した短い変数宣言構文が提供されています。これは、変数を宣言して初期化するための省略形です。一般的に型推論を用いる宣言では、var キーワードよりもこちらの方が多く使われます。

a := "A" // string型と推論される

fmt.Printf("Variable 'a' is of type %T\n", a)
// => Variable 'a' is of type string

型推論の基本例

型推論は右辺の値から型が推論され、左辺に自動的に適用されます。

i := 42      // 型は int と推論
f := 3.14    // 型は float64 と推論
s := "hello" // 型は string と推論
b := true    // 型は bool と推論

fmt.Printf("i: %T, f: %T, s: %T, b: %T\n", i, f, s, b)
// => i: int, f: float64, s: string, b: bool

このように、変数の初期化時に型を指定せずとも、Go のコンパイラは右辺のリテラルや式の結果から自動で適切な型を割り当てます。

配列やスライスでの型推論

配列やスライスにおいても型推論が行われます。以下の例では、リテラルの型に基づいてスライスの型が推論されます。

numbers := []int{1, 2, 3, 4, 5}   // 型は []int と推論
names := []string{"Alice", "Bob"} // 型は []string と推論

fmt.Printf("numbers: %T, names: %T\n", numbers, names)
// => numbers: []int, names: []string

関数の戻り値による型推論

関数の戻り値も型推論の対象です。関数から返された値の型に基づいて変数の型が推論されます。

package main

import "fmt"

func main() {
  num := getNumber() // 型は int と推論

  fmt.Printf("num: %T\n", num)
  // => num: int
}

func getNumber() int {
  return 10
}

定数と型推論

Go の定数は無定型定数と呼ばれ、使用される文脈に応じて適切な型に推論されます。

const x = 5

var y int = x     // x は int として推論される
var z float64 = x // 同じ x でも文脈によって float64 として扱われる

fmt.Printf("y: %T, z: %T\n", y, z)
// => y: int, z: float64

Go の定数は、型を明示していない限り、無限の精度を持つ抽象的な値として扱われます。そのため、型が指定されるまで実際の型は決定しません。代入先や使用される文脈に応じて異なる型に「自由に」キャストされます。

定数に型を明示すると、その定数はその型に固定されます。この場合、異なる型への暗黙のキャストは行われなくなります。

const e int = 5   // e は int 型として固定される
var f float64 = e // コンパイルエラー: int から float64 への暗黙の変換は許可されない
// =>  cannot use e (constant 5 of type int) as float64 value in variable declaration

マップ(map)の型推論

マップを初期化する際も、型推論を利用できます。

この例では、m2の宣言で型推論を使用しています。コンパイラは map[string]int 型であることを自動的に推論します。

// 明示的な型宣言
var m1 map[string]int = map[string]int{"a": 1, "b": 2}

// 型推論を使用
m2 := map[string]int{"x": 10, "y": 20}

fmt.Printf("m1: %v\n", m1)
// => m1: map[a:1 b:2]
fmt.Printf("m2: %v\n", m2)
// => m2: map[x:10 y:20]

構造体(struct)の型推論

構造体を初期化する際も、Go 言語の型推論機能を利用して明示的な型宣言を省略することができます。

この例では、p2の宣言で型推論を使用しています。コンパイラはPerson型であることを自動的に推論します。

// 明示的な型宣言
var p1 Person = Person{Name: "Alice", Age: 30}

// 型推論を使用
p2 := Person{Name: "Bob", Age: 25}

fmt.Printf("p1: %s, %+v\n", reflect.TypeOf(p1).Name(), p1)
// => p1: Person, {Name:Alice Age:30}
fmt.Printf("p2: %s, %+v\n", reflect.TypeOf(p2).Name(), p2)
// => p2: Person, {Name:Bob Age:25}

インターフェースの型推論

インターフェースの型推論とは、Go 言語がコンパイル時に、ある型がインターフェースを満たしているかどうかを自動的に判断する機能です。これにより、明示的な型宣言なしに、構造体やその他の型をインターフェースとして扱うことができます。

Go では、型がインターフェースを実装するために、その型がインターフェースで定義されたすべてのメソッドを持っている場合、そのインターフェースを満たしていると見なされます。その型が明示的にインターフェースを実装すると宣言する必要はありません。

package main

import "fmt"

// Writer インターフェースを定義
type Writer interface {
  Write([]byte) (int, error)
}

// ConsoleWriter 構造体を定義
type ConsoleWriter struct{}

// ConsoleWriter に Write メソッドを実装
func (cw ConsoleWriter) Write(data []byte) (int, error) {
  n, err := fmt.Println(string(data))
  return n, err
}

func main() {
  // ConsoleWriter のインスタンスを作成
  cw := ConsoleWriter{}

  // Writer インターフェースとして使用
  var w Writer = cw

  w.Write([]byte("Hello, Interface Type Inference!"))
}

まとめ

  • 型推論は、変数宣言時に型を明示せずにコンパイラが自動的に型を推測する機能
  • Go 言語は静的型付け言語だが、型推論によりコードの簡潔さを向上させる
  • 変数の初期化時に右側の値から型が自動的に推論される
  • := 演算子を使用した短い変数宣言構文が一般的に使用される
  • 配列、スライス、マップ、構造体などの複合型でも型推論が可能
  • 関数の戻り値も型推論の対象となる
  • 定数は無定型定数として扱われ、使用される文脈に応じて適切な型に推論される
  • インターフェースの型推論により、明示的な型宣言なしに構造体などをインターフェースとして扱える
  • 型推論はコードの可読性を向上させるが、複雑な式では明示的な型指定が推奨される


[Prev] Step 1-4: Arrays, Slices, Maps, Structs, make()

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級