Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Goクイズで学ぶメソッドセット

 Goクイズで学ぶメソッドセット

入門Go言語仕様輪読会 第3回 method setsに関する資料です。
https://gospecreading.connpass.com/event/208080/

下記のgo-talks上で見れば、スライド内のコードを実行できます!
https://go-talks.appspot.com/github.com/task4233/slide-decks/methodSets.slide

195f71c8183f8d27da66aa0618f293b6?s=128

task4233

April 15, 2021
Tweet

Transcript

  1. Goクイズで学ぶメソッドセット Goクイズで学ぶメソッドセット Go⾔語仕様輪読会 Go⾔語仕様輪読会 2021/04/15 2021/04/15 task4233 task4233

  2. ⾃⼰紹介 ⾃⼰紹介 task4233 (Takashi MIMA) task4233 (Takashi MIMA) 趣味と実益を兼ねてGoを書いています 趣味と実益を兼ねてGoを書いています

    Go本体へのコントリビュート経験があります Go本体へのコントリビュート経験があります (https://go-review.googlesource.com/c/go/+/288472) (https://go-review.googlesource.com/c/go/+/288472) 2 2
  3. 今回は、Goのメソッドセットという概念を扱います 今回は、Goのメソッドセットという概念を扱います ゴール ゴール インタフェースを「実装する」という概念を通して、 インタフェースを「実装する」という概念を通して、メソッドセットとは何なのか メソッドセットとは何なのか 理解すること 理解すること 理解するメリット

    理解するメリット Goの他の仕様を理解する助けになる Goの他の仕様を理解する助けになる Method calls - The Go Programming Language Specification Method calls - The Go Programming Language Specification (https://golang.org/ref/spec#Calls) (https://golang.org/ref/spec#Calls) Interface types - The Go Programming Language Specification Interface types - The Go Programming Language Specification (https://golang.org/ref/spec#Interface_types) (https://golang.org/ref/spec#Interface_types) 3 3
  4. おさらい︓メソッドとは おさらい︓メソッドとは レシーバを伴う関数 レシーバを伴う関数 4 4

  5. レシーバの型の制約 レシーバの型の制約 レシーバの型は、defined typeもしくはdefined typeのポインタ型でなくてはならない レシーバの型は、defined typeもしくはdefined typeのポインタ型でなくてはならない レシーバの型Tおよび*TのTを、receiver base

    typeと呼ぶ レシーバの型Tおよび*TのTを、receiver base typeと呼ぶ ただしreceiver base typeは、インタフェース型もしくはポインタ型であってはならない ただしreceiver base typeは、インタフェース型もしくはポインタ型であってはならない 5 5
  6. メソッドセットとは メソッドセットとは メソッドセットは、 メソッドセットは、型 型 に関連付けられた に関連付けられた メソッドの集合 メソッドの集合 のこと

    のこと 下記の例で、型 下記の例で、型 T T のメソッドセットと、型 のメソッドセットと、型 *T *T のメソッドセットについて考えてみます のメソッドセットについて考えてみます type Num int type Num int func (num Num) A1() { num++ } func (num Num) A1() { num++ } func (num Num) B1(val Num) { num += val } func (num Num) B1(val Num) { num += val } func (num *Num) A2() { *num++ } func (num *Num) A2() { *num++ } func (num *Num) B2(val Num) { *num += val } func (num *Num) B2(val Num) { *num += val } Run 6 6
  7. 型 Tのメソッドセット 型 Tのメソッドセット 型 型 T T のメソッドセットは、 のメソッドセットは、レシーバ型

    レシーバ型 T T で宣⾔された で宣⾔された 全てのメソッドの集合 全てのメソッドの集合 型 型 Num Num のメソッドセット のメソッドセット A1 A1 B1 B1 type Num int type Num int func (num Num) A1() { num++ } func (num Num) A1() { num++ } func (num Num) B1(val Num) { num += val } func (num Num) B1(val Num) { num += val } func (num *Num) A2() { *num++ } func (num *Num) A2() { *num++ } func (num *Num) B2(val Num) { *num += val } func (num *Num) B2(val Num) { *num += val } Run 7 7
  8. 型 *Tのメソッドセット 型 *Tのメソッドセット 型 型 *T *T のメソッドセットは、 のメソッドセットは、レシーバ型

    レシーバ型 T T または または *T *T で宣⾔された で宣⾔された 全てのメソッドの集合 全てのメソッドの集合 型 型 *Num *Num のメソッドセット のメソッドセット A1 A1 B1 B1 A2 A2 B2 B2 type Num int type Num int func (num Num) A1() { num++ } func (num Num) A1() { num++ } func (num Num) B1(val Num) { num += val } func (num Num) B1(val Num) { num += val } func (num *Num) A2() { *num++ } func (num *Num) A2() { *num++ } func (num *Num) B2(val Num) { *num += val } func (num *Num) B2(val Num) { *num += val } Run 8 8
  9. インタフェース型のメソッドセット インタフェース型のメソッドセット インタフェース型のメソッドセットは、その インタフェース型のメソッドセットは、その インタフェース定義で列挙される インタフェース定義で列挙される メソッドの集合 メソッドの集合 インタフェース型 インタフェース型

    I I のメソッドセット のメソッドセット A1 A1 B1 B1 A2 A2 B2 B2 type I interface { type I interface { A1() A1() B1(val int) B1(val int) A2() A2() B2(val int) B2(val int) } } Run 9 9
  10. メソッドセットの詳細な定義 メソッドセットの詳細な定義 インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド の集合 の集合 型 型 T T

    のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 型 型 *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される(今回は時間が⾜ (今回は時間が⾜ りないので割愛) りないので割愛) それ以外の型は、空のメソッドセットを持つ それ以外の型は、空のメソッドセットを持つ 10 10
  11. 型Tがインタフェース Iを「実装する」とは 型Tがインタフェース Iを「実装する」とは 型 型 T T のメソッドセットがインタフェース のメソッドセットがインタフェース

    I I のメソッドセットを全て含むこと のメソッドセットを全て含むこと 11 11
  12. インタフェースを「実装している」ときは代⼊が可能 インタフェースを「実装している」ときは代⼊が可能 下記の例で、型CatはAnimalインタフェースを「実装している」 下記の例で、型CatはAnimalインタフェースを「実装している」 インタフェースを「実装している」型は、Assignability(代⼊可能性)のルールにより、そ インタフェースを「実装している」型は、Assignability(代⼊可能性)のルールにより、そ のインタフェース型の変数に代⼊してもコンパイルエラーにならない のインタフェース型の変数に代⼊してもコンパイルエラーにならない package main

    package main var _ Animal = Cat{} var _ Animal = Cat{} type Animal interface { type Animal interface { MakeSound() string MakeSound() string } } type Cat struct{} type Cat struct{} func (Cat) MakeSound() string { func (Cat) MakeSound() string { return "meow" return "meow" } } func main() {} func main() {} Run 12 12
  13. インタフェースを「実装していない」ときの代⼊はコンパイルエラー インタフェースを「実装していない」ときの代⼊はコンパイルエラー 下記の例で、Cat型はAnimalインタフェースを「実装していない」ので、代⼊時にコンパイ 下記の例で、Cat型はAnimalインタフェースを「実装していない」ので、代⼊時にコンパイ ルエラーになる ルエラーになる package main package main

    var _ Animal = Cat{} var _ Animal = Cat{} type Animal interface { type Animal interface { MakeSound() string MakeSound() string } } type Cat struct{} type Cat struct{} func main() {} func main() {} Run 13 13
  14. 1. インタフェースの実装の基礎 1. インタフェースの実装の基礎 「実装している」か否かはメソッドセットを意識すると良い 「実装している」か否かはメソッドセットを意識すると良い 14 14

  15. Goクイズ︓インタフェース型のメソッドセット Goクイズ︓インタフェース型のメソッドセット Cat型はAnimalインタフェースを実装していますか︖ Cat型はAnimalインタフェースを実装していますか︖ package main package main var _

    Animal = Cat{} var _ Animal = Cat{} type Animal interface { type Animal interface { MakeSound() string MakeSound() string } } type Cat struct{} type Cat struct{} func (Cat) MakeSound() []byte { func (Cat) MakeSound() []byte { return []byte("meow") return []byte("meow") } } func main() {} func main() {} Run 15 15
  16. 解答と解説︓インタフェース型のメソッドセット 解答と解説︓インタフェース型のメソッドセット 返り値が異なっているのでメソッドセットが異なるから実装していない 返り値が異なっているのでメソッドセットが異なるから実装していない 仮引数リストが異なる場合も同様 仮引数リストが異なる場合も同様 package main package main

    var _ Animal = Cat{} var _ Animal = Cat{} type Animal interface { type Animal interface { MakeSound() string MakeSound() string } } type Cat struct{} type Cat struct{} func (Cat) MakeSound() []byte { func (Cat) MakeSound() []byte { return []byte("meow") return []byte("meow") } } func main() {} func main() {} Run 16 16
  17. 2. 型 T のインタフェース実装 2. 型 T のインタフェース実装 型 型

    T T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 17 17
  18. Goクイズ︓型 T のインタフェース実装 Goクイズ︓型 T のインタフェース実装 型 型 EmptyError EmptyError

    は、 は、 error error インタフェースを実装していますか︖ インタフェースを実装していますか︖ package main package main var _ error = EmptyError{} var _ error = EmptyError{} type error interface { type error interface { Error() string Error() string } } type EmptyError struct{} type EmptyError struct{} func (e *EmptyError) Error() string { func (e *EmptyError) Error() string { return "empty" return "empty" } } func main() {} func main() {} Run 18 18
  19. 解答と解説︓型 T のインタフェース実装 解答と解説︓型 T のインタフェース実装 型 型 EmptyError EmptyError

    は は error error インタフェースを「実装していない」 インタフェースを「実装していない」 型 型 EmptyError EmptyError のメソッドセットは空 のメソッドセットは空 error error インタフェースのメソッドセットは インタフェースのメソッドセットは Error Error package main package main var _ error = EmptyError{} var _ error = EmptyError{} type error interface { type error interface { Error() string Error() string } } type EmptyError struct{} type EmptyError struct{} // Error は型 *EmptyError のメソッドセットにのみ含まれる // Error は型 *EmptyError のメソッドセットにのみ含まれる func (e *EmptyError) Error() string { func (e *EmptyError) Error() string { return "empty" return "empty" } } func main() {} func main() {} Run 19 19
  20. コラム︓メソッド呼び出し時の特別ルール コラム︓メソッド呼び出し時の特別ルール x.m()というメソッド呼び出しについて、 x.m()というメソッド呼び出しについて、 xがaddressable xがaddressable で で &xのメソッドセットがmを含 &xのメソッドセットがmを含

    んでいる んでいる 場合 、x.m()は(&x).m()の省略形になる 場合 、x.m()は(&x).m()の省略形になる Address operators - The Go Programming Language Specification Address operators - The Go Programming Language Specification (https://golang.org/ref/spec#Address_operators) (https://golang.org/ref/spec#Address_operators) package main package main type error interface { type error interface { Error() string Error() string } } type EmptyError struct{} type EmptyError struct{} func (e *EmptyError) Error() string { func (e *EmptyError) Error() string { return "empty" return "empty" } } func main() { func main() { var emptyError EmptyError var emptyError EmptyError // 内部的に(&emptyError).Error() と同義 // 内部的に(&emptyError).Error() と同義 emptyError.Error() emptyError.Error() } } Run 20 20
  21. 3. 型 *T のインタフェース実装 3. 型 *T のインタフェース実装 型 型

    *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 21 21
  22. Goクイズ︓型 *T のインタフェースの実装 Goクイズ︓型 *T のインタフェースの実装 型 型 *EmptyError *EmptyError

    は、 は、 error error インタフェースを実装していますか︖ インタフェースを実装していますか︖ package main package main var _ error = &EmptyError{} var _ error = &EmptyError{} type error interface { type error interface { Error() string Error() string } } type EmptyError struct{} type EmptyError struct{} func (e EmptyError) Error() string { func (e EmptyError) Error() string { return "empty" return "empty" } } func main() {} func main() {} Run 22 22
  23. 解答と解説︓型 *T のインタフェースの実装 解答と解説︓型 *T のインタフェースの実装 型 型 *EmptyError *EmptyError

    は は error error インタフェースを「実装している」 インタフェースを「実装している」 型 型 *EmptyError *EmptyError のメソッドセットは のメソッドセットは Error Error error error インタフェースのメソッドセットは インタフェースのメソッドセットは Error Error package main package main var _ error = &EmptyError{} var _ error = &EmptyError{} type error interface { type error interface { Error() string Error() string } } type EmptyError struct{} type EmptyError struct{} // Error は型 *EmptyError のメソッドセットにも含まれる // Error は型 *EmptyError のメソッドセットにも含まれる func (e EmptyError) Error() string { func (e EmptyError) Error() string { return "empty" return "empty" } } func main() {} func main() {} Run 23 23
  24. 4. 空のインタフェース実装 4. 空のインタフェース実装 どんな型でも、デフォルトで空のメソッドセットを持つ どんな型でも、デフォルトで空のメソッドセットを持つ 24 24

  25. Goクイズ︓空のインタフェース Goクイズ︓空のインタフェース nil nil は、 は、 Empty Empty インタフェースを実装していますか︖ インタフェースを実装していますか︖

    package main package main type Empty interface{} type Empty interface{} func main() { func main() { var _ Empty = nil var _ Empty = nil } } Run 25 25
  26. 解答と解説︓空のインタフェース 解答と解説︓空のインタフェース インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 package main package main

    type Empty interface{} type Empty interface{} func main() { func main() { // 全ての値を代入可能 // 全ての値を代入可能 var _ Empty = nil var _ Empty = nil var _ Empty = 57 var _ Empty = 57 var _ Empty = "hoge" var _ Empty = "hoge" type Person struct { type Person struct { Name string Name string } } var _ Empty = Person{} var _ Empty = Person{} } } Run 26 26
  27. コラム︓interfaceの値を利⽤するためには型アサーションが必要 コラム︓interfaceの値を利⽤するためには型アサーションが必要 package main package main func main() { func

    main() { var _ interface{} = nil var _ interface{} = nil var Num interface{} = -1 var Num interface{} = -1 // Numと1の型が異なるので、invalidな式の演算が原因でcompile errorになる // Numと1の型が異なるので、invalidな式の演算が原因でcompile errorになる // var _ int = Num + 1 // var _ int = Num + 1 // intとNumのUnderlying typeが異なるので、Coversionできないことが原因でcompile errorになる // intとNumのUnderlying typeが異なるので、Coversionできないことが原因でcompile errorになる // var _ int = int(Num) + 1 // var _ int = int(Num) + 1 // type assertionすればOK // type assertionすればOK var _ int = Num.(int) + 1 var _ int = Num.(int) + 1 } } Run 27 27
  28. まとめ まとめ どんな型でもデフォルトで空のメソッドセットを持つ どんな型でもデフォルトで空のメソッドセットを持つ メソッドセットの定義 メソッドセットの定義 インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド の集合 の集合

    型 型 T T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 型 型 *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される それ以外の型は、空のメソッドセットを持つ それ以外の型は、空のメソッドセットを持つ 28 28
  29. Thank you Thank you Go⾔語仕様輪読会 Go⾔語仕様輪読会 2021/04/15 2021/04/15 task4233 task4233

    @task4233 @task4233 (http://twitter.com/task4233) (http://twitter.com/task4233)
  30. None