A Tour of Goをやるぞ(Methods and interfaces)

これをやる

A Tour of Go

型にメソッドを定義

funcと関数名の間にreceiverを定義することで型にメソッドを定義をできる。 メソッドはreceiverを持った関数のことである。

package main

import (
    "fmt"
)

func (test Test) Double() int {
    return test.X * test.X
}

type Test struct {
    X int
}

func main() {
    test := Test{2}
    fmt.Println(test.double())   
}

structでない型にも定義できる。

package main

import (
    "fmt"
)

type MyInt int

func (val MyInt) Double() MyInt {
    return val * val
}

func main() {
    value := MyInt(2)
    fmt.Println(value.Double())
}

receiverを持ったメソッドは型が定義されたパッケージ内でのみ定義できる。

Pointer receivers

Pointer receiversと共に定義したメソッドでは、構造体のインスタンスインスタンスという呼び名でいいのか?)に対して変更が可能。

package main

import (
    "fmt"
)

type Test struct {
    value int
}

func (v *Test) mutable() {
    v.value = v.value * 2
}

func (v Test) immutable() {
    v.value = v.value * 2
}

func main() {
    v := Test{3}
    v.immutable() // ここでvの値は変更されない
    fmt.Println(v)

    v.mutable() // ここでは変更される
    fmt.Println(v)
}

関数に渡した値に破壊的変更をする場合も同じノリ

package main

import (
    "fmt"
)

type Test struct {
    value int
}

func mutable(v *Test) {
    v.value = v.value * 2
}

func immutable(v Test) {
    v.value = v.value * 2
}

func main() {
    v := Test{3}
    immutable(v) // ここでvの値は変更されない
    fmt.Println(v)

    mutable(&v) // ここでは変更される
    fmt.Println(v)
}
package main

type Test struct {
    value int
}

func mutable(v *Test) {
    v.value = v.value * 2
    print("\n")
    print(v) // ポインタA
}

func immutable(v Test) {
    v.value = v.value * 2
    print("\n")
    print(&v) // ポインタB(コピーなので呼び出し元とポインタが変わる)
}

func main() {
    v := Test{3}
    print("\n")
    print(&v) // ポインタA
    immutable(v) // ここでvの値は変更されない
    mutable(&v) // ここでは変更される
}

要するに

  • ポインタで渡した場合は同じ参照(なぜならポインタで渡したから)
  • 値を直接渡した場合、関数の中で参照できる値は渡した値そのものではなくコピー

という感じ。

先に気になって別の話で書いたがreturnで呼び出し元に戻される値も値そのものではなくコピーになる。 deferの中でmutationな操作をする関数を読んで戻り値を変更しても、戻り値に影響がないのは何故だろう - チョキチョキかにさん

Pointer receiversと共に定義したメソッドはポインタ経由でも値経由でも呼び出せる

package main

import "fmt"

type Test struct {
    test int
}

func (value *Test) double() {
    value.test = value.test * 2
}

func main() {
    v := Test{1}
    v.double()
    fmt.Println(v) // 2
    (&v).double()
    fmt.Println(v) // 4
}

いずれも破壊的な操作が可能。(ポインタ経由での呼び出しは、値経由での呼び出しと解釈されるとのこと)

Pointer receiverを使う理由

  • 破壊的変更ができる
  • メソッド呼び出し時に構造体をコピーするコストを回避できる

value receiverとpointer receiverを混在させるべきではない。(理由は後ほど出てくるらしい)

インターフェース

インターフェース型はメソッドシグネチャの集合である。

package main

import (
    "fmt"
)

type TestInterface1 interface {
    print()
}

type TestInterface2 interface {
    printDouble()
}

type Test1 struct{
    test int
}

type Test2 struct{
    test int
}

func (v Test1) print() {
    fmt.Println(v)
}

func (v Test2) print() {
    fmt.Println(v)
}

func (v Test1) printDouble() {
    fmt.Println(v)
}

func main() {
    var a TestInterface1
    var b TestInterface2

    a = Test1{1}
    b = Test1{2}

    a.print()
    b.printDouble()

    b.print() // bの型にprintは定義されてないのでコンパイルエラー

    var c TestInterface2
    c = Test2{1} // Test2にprintDoubleは定義されていないのでコンパイルエラー

}

要するに、

  • interfaceで定義されたシグネチャと同じシグネチャのメソッドが定義されたstructの値は、そのinterface型に代入できる
  • そのinterface型の変数経由で、interfaceに定義したシグネチャと同じメソッドを、struct経由で呼び出せる

と言った感じ。

初期化してない型のメソッドを呼んだ場合の挙動

NullPointerExceptionになるのではなく、receiverがnilになる。

package main

type Test struct {
    value int
}

type I interface {
    double() int    
}

func (test *Test) double() int {
    if test == nil {
        return 0
    }
    return test.value * 2
}

func main() {
    var a *Test // 値が初期化されていないポインタ
    a.double() // ここでNullPointerExceptionにならず、double関数のtestがnilになる
    var b I
    b = a
    b.double() // インターフェースの場合も同様

}

型の情報もないinterface(nil interface)のメソッドを呼ぶ場合

セグメンテーション違反で実行時エラーになる。

package main

type Test struct {
    value int
}

type I interface {
    double() int    
}

func main() {
    var b I
    b.double() // 型情報すらない場合ここでセグメンテーション違反で実行時エラー

}

empty interface

メソッドの定義がないインターフェースのこと。あらゆる型の値を代入できる。型のわからない値をハンドリングするために使う。

Type assertions

インターフェースの元の値へのアクセスを提供する。

package main

import "fmt"

type TestI interface {
    test()
}

type TestS struct {
    value string
}

func (test TestS) test() {
    return
}

func main() {
    var i TestI = TestS{"test"}

    s := i.(TestS)
    fmt.Println(s)

    s, ok := i.(TestS)
    fmt.Println(s, ok) // 2値で受けるとinterfaceが特定の型を保持しているかどうかを判定できる
}
package main

import "fmt"

type TestI interface {
    test()
}

type TestS struct {
    value string
}

func (test TestS) test() {
    return
}

func main() {
    var i TestI = TestS{"test"}

    f, ok := i.(float64) // impossible type assertion:float64 does not implement TestI (missing test method)
    fmt.Println(f, ok)
}

このケースではエラーになる。Type assertionで指定する型は、対象の変数を宣言した際のインターフェースに対応している必要がある。

package main

import "fmt"

type TestI interface {
    test()
}

type TestS struct {
    value string
}

type TestSS struct {
    value string
}

func (test TestS) test() {
    return
}

func (test TestSS) test() {
    return
}

func main() {
    var i TestI = TestS{"test"}
    f, ok := i.(TestSS)
    fmt.Println(f, ok)
}

こうするとokにfalseが入ってくる。

Type switches

インターフェースの型による分岐を表現できる。

package main

import "fmt"

type I interface{}

func main() {
    var a interface{}
    a = "test"
    switch t := a.(type) {
    case string:
        fmt.Println("string", t)
    case int:
        fmt.Println("int", t)
    }
}

a.(type)はぱっと見型情報を取得するための構文に見えるがType switchの中でないとコンパイルエラーになる。

ここでtにはcaseでマッチした型でtype assertionした場合のaが入ってくる。

package main

import "fmt"

type I interface{}

func acceptString(value string) {
    fmt.Println(value)
}

func acceptInt(value int) {
    fmt.Println(value)
}

func main() {
    var a interface{}
    a = "test"
    switch t := a.(type) {
    case string:
        acceptString(t) // ここではtはstring
    case int:
        acceptInt(t) // ここではtはint
    }
}

要するにa.(type)はswitch用の特定の型に固定しないtype assertionという感じ。

Stringers

typeがString() stringを実装している場合、そのtypeはStringerインターフェースに対応している。(対応という言い方はあっているのだろうか) JavaでいうとtoStringだけ持ってるインターフェースがObjectクラスから分離してる感じ。

package main

import "fmt"

type Metalica struct {
}

func (p Metalica) String() string {
    return fmt.Sprintf("WOOOOOO YEAHHHHH")
}

func main() {
    a := Metalica{}
    fmt.Println(a) // Metalicaをレシーバーに持ったString()の結果が出力される
}

Exercise: Stringers

package main

import "fmt"

type IPAddr [4]byte

func (v IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", v[0], v[1], v[2], v[3])
}

func main() {
    hosts := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for name, ip := range hosts {
        fmt.Printf("%v: %v\n", name, ip)
    }
}

なんとなくStringersのレシーバーはポインタにしたい気がするがポインタにするとfmt.PrintfでString()が呼ばれなかった。 インターフェースについて微妙に理解できてない感がある。

Errors

error interfaceというのがいて、それに対応したtypeはErrorとして使える。

type error interface {
    Error() string
}
package main

import (
    "fmt"
)

type MyError struct {
    msg string
}

func (v MyError) Error() string {
    return v.msg
}

func run() error {
    return MyError{"あーあ"}
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}
package main

import (
    "fmt"
)

type DivisionByZeroError struct {
    message string
}

// errorインターフェースの実装
func (err DivisionByZeroError) Error() string {
    return err.message
}

func divide(d int, n int) (int, error) {
    if n == 0 {
        return 0, DivisionByZeroError{"lol"}
    }
    return d / n, nil // エラーでない場合はerrorにnilを返す
}

func main() {
    result, err := divide(3, 0); // 結果をerrを受けて、errがnilかどうかによって関数呼び出しが成功したかどうかを判定する
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(result)
}

いわゆるエラーハンドリングについてはこんな感じになるようだ。

Exercise: Errors

package main

import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprint("cannot Sqrt negative number ", float64(e))
}

func Sqrt(x float64) (float64, error) {

    if x < 0 {
        return x, ErrNegativeSqrt(x)
    }

    z := float64(1)
    for i := 0; i < 10; i++ {
        z -= calc(z, x)

        if math.Abs(x-z*z) < 0.000001 {
            fmt.Println(i)
            return z, nil
        }
    }
    return z, nil
}

func calc(z float64, x float64) float64 {
    return (z*z - x) / (2 * z)
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

初期化時のType()とType{}の違いがわかっていない。

Exercise: Readers

package main

import "golang.org/x/tour/reader"

type MyReader struct{}

func (r MyReader) Read(bs []byte) (int, error) {
    counta := 0
    for i, _ := range bs {
        bs[i] = 'A'
        counta++
    }
    return counta, nil
}

func main() {
    reader.Validate(MyReader{})
}

Exercise: rot13Reader

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func (r *rot13Reader) Read(b []byte) (int, error) {
    n, e := r.r.Read(b)
    if e != nil {
        return 0, e
    }

    for i, _ := range b {
        // fmt.Println(string(b[i]))

        if b[i] >= 'A' && b[i] <= 'Z' {
            b[i] = b[i] + 13
            if b[i] > 'Z' {
                b[i] = b[i] - 26
            }
        }

        if b[i] >= 'a' && b[i] <= 'z' {
            b[i] = b[i] + 13
            if b[i] > 'z' {
                b[i] = b[i] - 26
            }
        }
    }
    return n, nil
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

Exercise: Images

package main

import (
    "golang.org/x/tour/pic"
    "image"
    "image/color"
)

type Image struct {
    w     int
    h     int
    color uint8
}

func (i Image) ColorModel() color.Model {
    return color.RGBAModel
}

func (i Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, i.w, i.h)
}

// この座標この色的な
func (i Image) At(x, y int) color.Color {
    return color.RGBA{uint8(x), uint8(y), i.color, i.color}
}

func main() {
    m := Image{100, 100, 100}
    pic.ShowImage(m)
}