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

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

task4233
April 15, 2021

 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

task4233

April 15, 2021
Tweet

More Decks by task4233

Other Decks in Technology

Transcript

  1. ⾃⼰紹介 ⾃⼰紹介 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
  2. 今回は、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
  3. レシーバの型の制約 レシーバの型の制約 レシーバの型は、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
  4. メソッドセットとは メソッドセットとは メソッドセットは、 メソッドセットは、型 型 に関連付けられた に関連付けられた メソッドの集合 メソッドの集合 のこと

    のこと 下記の例で、型 下記の例で、型 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
  5. 型 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
  6. 型 *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
  7. メソッドセットの詳細な定義 メソッドセットの詳細な定義 インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド の集合 の集合 型 型 T T

    のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 型 型 *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される(今回は時間が⾜ (今回は時間が⾜ りないので割愛) りないので割愛) それ以外の型は、空のメソッドセットを持つ それ以外の型は、空のメソッドセットを持つ 10 10
  8. 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
  9. 解答と解説︓インタフェース型のメソッドセット 解答と解説︓インタフェース型のメソッドセット 返り値が異なっているのでメソッドセットが異なるから実装していない 返り値が異なっているのでメソッドセットが異なるから実装していない 仮引数リストが異なる場合も同様 仮引数リストが異なる場合も同様 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
  10. 2. 型 T のインタフェース実装 2. 型 T のインタフェース実装 型 型

    T T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 17 17
  11. 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
  12. 解答と解説︓型 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
  13. コラム︓メソッド呼び出し時の特別ルール コラム︓メソッド呼び出し時の特別ルール 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
  14. 3. 型 *T のインタフェース実装 3. 型 *T のインタフェース実装 型 型

    *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 21 21
  15. 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
  16. 解答と解説︓型 *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
  17. 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
  18. 解答と解説︓空のインタフェース 解答と解説︓空のインタフェース インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 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
  19. コラム︓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
  20. まとめ まとめ どんな型でもデフォルトで空のメソッドセットを持つ どんな型でもデフォルトで空のメソッドセットを持つ メソッドセットの定義 メソッドセットの定義 インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド の集合 の集合

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

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