$30 off During Our Annual Pro Sale. View Details »

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. Goクイズで学ぶメソッドセット
    Goクイズで学ぶメソッドセット
    Go⾔語仕様輪読会
    Go⾔語仕様輪読会
    2021/04/15
    2021/04/15
    task4233
    task4233

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  10. メソッドセットの詳細な定義
    メソッドセットの詳細な定義
    インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド
    インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド
    の集合
    の集合

    型 T
    T のメソッドセットは、レシーバ型
    のメソッドセットは、レシーバ型 T
    T で宣⾔された全てのメソッドの集合
    で宣⾔された全てのメソッドの集合

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

    View Slide

  11. 型Tがインタフェース Iを「実装する」とは
    型Tがインタフェース Iを「実装する」とは

    型 T
    T のメソッドセットがインタフェース
    のメソッドセットがインタフェース I
    I のメソッドセットを全て含むこと
    のメソッドセットを全て含むこと
    11
    11

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  17. 2. 型 T のインタフェース実装
    2. 型 T のインタフェース実装

    型 T
    T のメソッドセットは、レシーバ型
    のメソッドセットは、レシーバ型 T
    T で宣⾔された全てのメソッドの集合
    で宣⾔された全てのメソッドの集合 17
    17

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  21. 3. 型 *T のインタフェース実装
    3. 型 *T のインタフェース実装

    型 *T
    *T のメソッドセットは、レシーバ型
    のメソッドセットは、レシーバ型 T
    T または
    または *T
    *T で宣⾔された全てのメソッドの集合
    で宣⾔された全てのメソッドの集合 21
    21

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  28. まとめ
    まとめ
    どんな型でもデフォルトで空のメソッドセットを持つ
    どんな型でもデフォルトで空のメソッドセットを持つ
    メソッドセットの定義
    メソッドセットの定義
    インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド
    インタフェース型のメソッドセットは、そのインタフェース定義で列挙されるメソッド
    の集合
    の集合

    型 T
    T のメソッドセットは、レシーバ型
    のメソッドセットは、レシーバ型 T
    T で宣⾔された全てのメソッドの集合
    で宣⾔された全てのメソッドの集合

    型 *T
    *T のメソッドセットは、レシーバ型
    のメソッドセットは、レシーバ型 T
    T または
    または *T
    *T で宣⾔された全てのメソッドの集合
    で宣⾔された全てのメソッドの集合
    埋め込みフィールドを持つ構造体については、更なる規則が適⽤される
    埋め込みフィールドを持つ構造体については、更なる規則が適⽤される
    それ以外の型は、空のメソッドセットを持つ
    それ以外の型は、空のメソッドセットを持つ
    28
    28

    View Slide

  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)

    View Slide

  30. View Slide