今日も今日とて、『実用 Go言語』の読書録を書いていきます。 前回は1章の記録を書きましたが、今回は2章定義型〜3章構造体まで書いていきます。
定義型
まずは、例から。
こんなふうに型を定義することができる。
type Opcode byte type Definition struct { Name string OperandWidths []int }
メソッドとレシーバ
定義した型に対して、メソッドを持たせることもできる。 レシーバとは、構造体のフィールドやその他のデータ型にアクセスする方法を提供するメソッドの引数。
func (s HTTPStatus) String()
のs HTTPStatus
のこと。sをレシーバ変数と呼ぶ。
package extend type HTTPStatus int const ( StatusOK HTTPStatus = 200 StatusUnauthorized HTTPStatus = 401 ) func (s HTTPStatus) String() string { switch s { case StatusOK: return "OK" case StatusUnauthorized: return "Unauthorized" default: return fmt.Sprintf("HTTPStatus(%d)", s) } }
列挙型への型定義
また、列挙型のレシーバにロジックを実装することもいける。
type Season int const ( Peak Season = iota + 1 Normal Off ) func (s Season) Price(price int) int { if s == Peak { return price + 200 } return price }
一応、テストも書いてみましょう
func TestPrice(t *testing.T) { tests := []struct { season Season got int want int }{ {Peak, 1000, 1200}, {Normal, 1000, 1000}, {Off, 1000, 1000}, } for _, tt := range tests { got := tt.season.Price(tt.got) if got != tt.want { t.Errorf("Price(%v, %v) = %v; want %v", tt.season, tt.got, got, tt.want) } } }
構造体
続いて、構造体。
Goにはクラスがない。代わりに?というのが正しいかは知らないが、構造体を提供している。
type Lexer struct { input string position int readPosition int ch byte } func New(input string) *Lexer { l := &Lexer{input: input} l.readChar() return l }
特徴
フィールドの読み書きを禁止してない。
PHPであれば、メンバ変数はプライベートにして、メソッド経由で読み書きを行ったりするが、Goの場合は読み書き可能。
インスタンス化
構造体をインスタンス化する方法は3種類提供されている。
① new b1 := new(Book) ② var 変数宣言 var b2 Book ③ 複合リテラル b3 := &Book{ Title: "親孝行プレイ", Author : "みうらじゅん" }
値を入れないで生成したフィールドは、ゼロ値で初期化される。
構造体のメソッド、レシーバ
レシーバには、値レシーバーとポインタレシーバの2種類がある。
type Struct struct { v int } // 値レシーバー func (s Struct) SetValue(v int) { s.v = v } // ポインタレシーバ func (s *Struct) SetValuePointer(v int) { s.v = v }
テストコードで挙動を確認してみましょう。
func TestSetValue(t *testing.T) { s := Struct{v: 0} s.SetValue(100000) if s.v != 0 { t.Errorf("SetValue failed, got: %d, want: 0", s.v) } } func TestSetValuePointer(t *testing.T) { s := Struct{v: 0} s.SetValuePointer(10) if s.v != 10 { t.Errorf("SetValuePointer failed, got: %d, want: 0", s.v) } }
値レシーバーとポインタレシーバで、挙動が違うことがわかりますね。
実装方法
構造体を実装する際には、
- ポインターか、値か
- イミュータブルか、ミュータブルか
- ゼロ値の保証をどうするか?
という判断のポイントがある。
困ったらまずは、「ポインタで」「ミュータブルで」「ファクトリー関数で動作」という選択肢が、一番問題が少ないそうだ。
まとめ
以上、2章定義型〜3章構造体についてざっと気になった箇所について、まとめていきました。構造体にタグを埋め込む、ジェネリクスあたりは、雰囲気だけ理解して飛ばしました(必要に迫られたら復讐する
実装の選択肢とか継承がないなどなど、興味深く読めました。
次はインターフェースです。興奮してきますね
僕から以上。あったかくして寝ろよ