Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

⾃⼰紹介 ⾃⼰紹介 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

Slide 3

Slide 3 text

今回は、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

Slide 4

Slide 4 text

おさらい︓メソッドとは おさらい︓メソッドとは レシーバを伴う関数 レシーバを伴う関数 4 4

Slide 5

Slide 5 text

レシーバの型の制約 レシーバの型の制約 レシーバの型は、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

Slide 6

Slide 6 text

メソッドセットとは メソッドセットとは メソッドセットは、 メソッドセットは、型 型 に関連付けられた に関連付けられた メソッドの集合 メソッドの集合 のこと のこと 下記の例で、型 下記の例で、型 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

Slide 7

Slide 7 text

型 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

Slide 8

Slide 8 text

型 *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

Slide 9

Slide 9 text

インタフェース型のメソッドセット インタフェース型のメソッドセット インタフェース型のメソッドセットは、その インタフェース型のメソッドセットは、その インタフェース定義で列挙される インタフェース定義で列挙される メソッドの集合 メソッドの集合 インタフェース型 インタフェース型 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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

型Tがインタフェース Iを「実装する」とは 型Tがインタフェース Iを「実装する」とは 型 型 T T のメソッドセットがインタフェース のメソッドセットがインタフェース I I のメソッドセットを全て含むこと のメソッドセットを全て含むこと 11 11

Slide 12

Slide 12 text

インタフェースを「実装している」ときは代⼊が可能 インタフェースを「実装している」ときは代⼊が可能 下記の例で、型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

Slide 13

Slide 13 text

インタフェースを「実装していない」ときの代⼊はコンパイルエラー インタフェースを「実装していない」ときの代⼊はコンパイルエラー 下記の例で、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

Slide 14

Slide 14 text

1. インタフェースの実装の基礎 1. インタフェースの実装の基礎 「実装している」か否かはメソッドセットを意識すると良い 「実装している」か否かはメソッドセットを意識すると良い 14 14

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

解答と解説︓インタフェース型のメソッドセット 解答と解説︓インタフェース型のメソッドセット 返り値が異なっているのでメソッドセットが異なるから実装していない 返り値が異なっているのでメソッドセットが異なるから実装していない 仮引数リストが異なる場合も同様 仮引数リストが異なる場合も同様 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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

解答と解説︓型 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

Slide 20

Slide 20 text

コラム︓メソッド呼び出し時の特別ルール コラム︓メソッド呼び出し時の特別ルール 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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

解答と解説︓型 *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

Slide 24

Slide 24 text

4. 空のインタフェース実装 4. 空のインタフェース実装 どんな型でも、デフォルトで空のメソッドセットを持つ どんな型でも、デフォルトで空のメソッドセットを持つ 24 24

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

解答と解説︓空のインタフェース 解答と解説︓空のインタフェース インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ インタフェース以外の全ての型は、デフォルトで空のメソッドセットを持つ 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 空のインタフェースは空のメソッドセットを持つので、全ての型の値を代⼊可能 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

Slide 27

Slide 27 text

コラム︓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

Slide 28

Slide 28 text

まとめ まとめ どんな型でもデフォルトで空のメソッドセットを持つ どんな型でもデフォルトで空のメソッドセットを持つ メソッドセットの定義 メソッドセットの定義 インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド の集合 の集合 型 型 T T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 型 型 *T *T のメソッドセットは、レシーバ型 のメソッドセットは、レシーバ型 T T または または *T *T で宣⾔された全てのメソッドの集合 で宣⾔された全てのメソッドの集合 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される 埋め込みフィールドを持つ構造体については、更なる規則が適⽤される それ以外の型は、空のメソッドセットを持つ それ以外の型は、空のメソッドセットを持つ 28 28

Slide 29

Slide 29 text

Thank you Thank you Go⾔語仕様輪読会 Go⾔語仕様輪読会 2021/04/15 2021/04/15 task4233 task4233 @task4233 @task4233 (http://twitter.com/task4233) (http://twitter.com/task4233)

Slide 30

Slide 30 text

No content