A Tour of Goを試す(続き)
昨日のA Tour of Goを試すの続き。
"A Tour of Go"の「Methods and interfaces」と「concurrency」を読んだメモ。
メソッド
- Goにはクラスがない!!
- 型にメソッドを定義できる
-
func
キーワードとメソッド名の間にレシーバ引数としてレシーバを記述する
引数なので通常の関数として記述できる - 任意の型(組み込み型)にもメソッドを宣言可能。
-
type
で定義する必要がある。 - レシーバ型が同じパッケージにある必要がある。
-
ポインタレシーバ
- ポインタレシーバを持つメソッドはレシーバが指す変数を変更する。
- 変数レシーバは元の変数のコピーを操作する
- メソッドがポインタレシーバである場合、呼び出し時に、変数、または、ポインタのいずれかのレシーバとして取ることができます
- ポインタレシーバは引数として変数が渡された時にポインタとして解釈する
- メソッドが変数レシーバである場合、呼び出し時に、変数、または、ポインタのいずれかのレシーバとして取ることができます
- 変数レシーバは引数としてポインタが渡された時に、変数にアクセスする
ポインタレシーバを使う理由
- レシーバが指す先の変数を変更するため
- メソッドの呼び出し毎に変数がコピーされるのを避けるため
インターフェース
interface(インタフェース)型は、メソッドのシグニチャの集まりで定義します。
暗黙的
- 明示的に実装を宣言する必要はなく、型にメソッドを定義することでインターフェースを満たす
インターフェース値
- インターフェースの値のメソッドを呼び出すと、その基底型の同じ名前のメソッドが実行される
nil
の場合のインターフェース
具体的な値が- インターフェースの具体的な値が
nil
の場合、例外を引き起こさずにnil
をレシーバとして呼び出される
nil
インターフェース
- インターフェースが
nil
のものはメソッドを呼び出すとランタイムエラーになる
空インターフェース
interface{}
:ゼロ個のメソッドを指定されたインターフェース型
- 任意の型の値を保持できる
- 未知の型の値を扱うコードで使用される
型アサーション
インターフェースの基になる値を利用する
t := i.(T)
インターフェースの値i
が具体的な型T
を保持し、基になるT
の値を変数t
に代入する。
i
がT
を保持していない場合、この文はpanic
を引き起こします。
t, ok := i.(T)
インターフェースの値が特定の型を保持しているかテストする。
i
がT
を保持していれば、t
は基になる値、ok
はtrue
になります。
そうでなければ、ok
はfalse
、t
は型T
のゼロ値になりpanic
は起きません。
型switch
型switchのcaseは型を指定し、それらの値は指定されたインターフェースの値が保持する値の型と比較されます。
switch v := i.(type) { // `type`キーワードを使用する
case int:
// process
default:
// caseに一致しなかった場合
// 変数`v`は同じインターフェース型で値は`i`
}
Stringers
string
として表現することができる型で、fmt
パッケージに定義されている。
type Stringer interface {
String() string
}
演習(Stringers)
type IPAddr [4]byte
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
エラー
エラーの状態をerror
値で表現
type error interface {
Error() string
}
通常、関数がerror
変数を返し、呼び出し元はエラーがnil
か判定してハンドリングする。
演習(エラー)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return x, ErrNegativeSqrt(x)
}
...
}
Readers
io パッケージは、データストリームを読むことを表現する io.Reader インタフェースを規定しています。
Goの標準ライブラリには、ファイル、ネットワーク接続、圧縮、暗号化などで、このインタフェースが実装されている
// データを与えられたバイトスライスへ入れ、入れたバイトのサイズとエラーの値を返します。
// ストリームの終端は、`io.EOF`のエラーで返します。
func (T) Read(b []byte) (n int, err error)
演習(Readers)
func (r MyReader) Read(b []byte) (int, error) {
var e error
n := 0
for n < len(b) {
b[n] = 'A'
n++
}
return n, e
}
演習(rot13Reader)
image
// imageインターフェースの定義
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
演習(image)
ここから 「concurrency」 の章
goroutines
goroutine (ゴルーチン)は、Goのランタイムに管理される軽量なスレッドです。
go f(x, y, z)
で新しいgoroutine
が実行される。
f, x, y, zの評価は実行元(current)の
goroutine
で実行され、fの実行は新しいgoroutine
で実行されます。
goroutine
は、同じアドレス空間で実行されるため、共有メモリへのアクセスは必ず同期する必要があります。
チャネル
チャネル(Channel)型
は、チャネルオペレータの<-
を用いて値の送受信ができる通り道です。
ch <- v // `v`をチャネル`ch`へ送信する
v := <-ch // `ch`から受信した変数を`v`へ割り当てる
チャネルはmake関数で生成。
ch := make(chan int)
片方が準備できるまで送受信はブロックされ、goroutine
の同期を可能にする。
バッファチャネル
バッファを持つチャネルを初期化
// 2つ目の引数はバッファの長さ
ch := make(chan int, 100)
チャネルのクローズ
- 送信側は
close
を呼び、受信側はclose
されているか確認する
// 受信する値がない、かつチャネルが閉じているなら、`ok`は`false`
v, ok := <-ch
select
-
select
ステートメントは、goroutine
を複数の通信操作で待たせます。 -
select
は、複数あるcase
のいずれかが準備できるようになるまでブロックし、準備ができたcase
を実行します。 -
複数の
case
の準備ができている場合、case
はランダムに選択されます。 -
どの
case
も準備ができていないのであれば、select
の中のdefault
が実行されます。
演習(二分木)
排他制御
- sync.Mutex
- Lock、Unlock
Discussion