Goことはじめ3
第3章 型システム―型を用いた安全なプログラミング:はじめてのGo―シンプルな言語仕様,型システム,並行処理|gihyo.jp … 技術評論社
より学んだことメモ。
package main import ( "fmt" ) // // 型の拡張 // 同じ型でも違う意味を持つもの(同じintでもIDを表すものと優先度を表すものとか)を混同しちゃうとバグになるようなときに使う。 // type ID int type Priority int func ProcessTask(id ID, priority Priority) { fmt.Println(id, priority); } func RunProcessTask() { fmt.Println("型の拡張 ---------- ") var id ID = 3 var priority Priority = 5 // typeが違うのでコンパイルエラーする // ProcessTask(priority, id) // typeが適合するのでこっちは通る ProcessTask(id, priority) } // // 構造体 // type Task struct { ID int Detail string // パッケージ外にpublicな属性は先頭大文字 done bool // パッケージ内でprivateな属性は先頭小文字 } func RunTask() { fmt.Println("構造体 ---------- ") // Task構造体の初期化 // 値を与えなかった場合はゼロ値で初期化される var task Task = Task{ ID: 1, Detail: "buy the milk", done: true, } // 定義順に値を渡すことでフィールド名を省略 // var task Task = Task{1, "buy the milk", true} fmt.Println(task.ID) fmt.Println(task.Detail) fmt.Println(task.done) } // // ポインタ型 // 引数の方を*Taskとすることでポインタを渡せる // これで参照渡しができる // func Finish(task *Task) { task.done = true } func RunFinish() { fmt.Println("ポインタ型 ---------- ") // &Taskでポインタを渡せる task := &Task{done: false} Finish(task) fmt.Println(task.done) // true } // // 構造体の初期化 // コンストラクタがないのでNew*というメソッドを作って初期化するのが通例 // func NewTask(id int, detail string) *Task { // new()メソッドは // 構造体のフィールドをすべてゼロ値で初期化して返す var task *Task = new(Task) task.ID = id task.Detail = detail return task } func RunNewTask() { fmt.Println("構造体の初期化 ---------- ") task := NewTask(1, "buy the milk") fmt.Printf("%+v\n", task) } // // 構造体にメソッドを追加する // func (task Task) String() string { // レシーバのコピーがわたされるので // ここで値を変えてもオリジナルの値は変わらない str := fmt.Sprintf("%d) %s", task.ID, task.Detail) return str } func (task *Task) Finish() { // ポインタ渡しなのでオリジナルの値が変わる task.done = true } func RunTaskFunc() { fmt.Println("構造体のメソッド追加 ---------- ") task := NewTask(1, "buy the milk") fmt.Printf("%s\n", task) task.Finish() fmt.Printf("%s\n", task.done) } // // インターフェース // // インターフェイスの定義 // 単純なインターフェースは*erという名前をつけるのが通例 type Stringer interface { String() string } // 引数にインターフェースを指定することで // 必要なメソッドを実装していると判断する func Print(stringer Stringer) { fmt.Println(stringer.String()) } // 何も定義しないインターフェースを使うと // どのような型も渡せるメソッドを作れる func Do(e interface{}) { // do something } type StringStruct struct { Text string } func (ss *StringStruct) String() string { return ss.Text; } func NewStringStruct(text string) *StringStruct { return &StringStruct{ Text: text, } } func RunInterface() { ss := NewStringStruct("インターフェース ---------- ") Print(ss) } // // 型の埋め込み // 継承の代わりになるもの // 下記ではUserをUserTaskに埋め込む例 // type User struct { FirstName string LastName string } func (u *User) FullName() string { fullname := fmt.Sprintf("%s %s", u.FirstName, u.LastName) return fullname } func NewUser(firstName, lastName string) *User { return &User{ FirstName: firstName, LastName: lastName, } } type UserTask struct { ID int Detail string done bool *User // Userを埋め込む } func NewUserTask(id int, detail, firstName, lastName string) *UserTask { task := &UserTask { ID: id, Detail: detail, done: false, User: NewUser(firstName, lastName), } return task } func RunEmbed() { fmt.Println("型の埋め込み ---------- ") task := NewUserTask(1, "buy the milk", "Jxck", "Daniel") // UserTaskにUserのフィールドが埋め込まれている fmt.Println(task.FirstName) fmt.Println(task.LastName) // UserTaskにUserのメソッドが埋め込まれている fmt.Println(task.FullName()) // UserTaskから埋め込まれたUser自体にもアクセス可能 fmt.Println(task.User) } // // 型の変換 // func CastFunc() { // キャスト var i uint8 = 3 var j uint32 = uint32(i) // uint8 -> uint32 fmt.Println(j) var s string = "abc" var b []byte = []byte(s) // string -> []bytes fmt.Println(b) // キャストに失敗した場合はパニックが発生する // a := int("a") } func TypeAssertionFunc(value interface{}) { // TypeAssertion // 第1戻り値はその型に変換された値が返り、 // 第2戻り値は成否を返す // (第2戻り値を指定しなかった場合はパニック起こる) s, ok := value.(string) if ok { fmt.Printf("value is string: %s\n", s) } else { fmt.Printf("value is not string\n") } } func TypeSwitchFunc(value interface{}) { switch v := value.(type) { case string: fmt.Printf("value is string: %s\n", v) case int: fmt.Printf("value is int: %d\n", v) case Stringer: fmt.Printf("value is Stringer: %s\n", v) } } func RunType() { fmt.Println("型の変換 ---------- ") CastFunc() TypeAssertionFunc("StringValue") TypeAssertionFunc(1) TypeSwitchFunc("StringValue!!") TypeSwitchFunc(10) } func main() { RunProcessTask() RunTask() RunFinish() RunNewTask() RunTaskFunc() RunInterface() RunEmbed() RunType() }
チャネルにバッファを設けて処理ブロックを制御
package main import ( "fmt" "time" ) func main() { // バッファを3として設定 ch := make(chan string, 3) go func() { time.Sleep(time.Second) s := <-ch // 1秒後にデータを読み出す fmt.Printf("get %s\n", s) }() ch <- "a" // ブロックしない fmt.Println("put a") ch <- "b" // ブロックしない fmt.Println("put b") ch <- "c" // ブロックしない fmt.Println("put c") ch <- "d" // 1秒後にデータが読み出されるまでブロック fmt.Println("put d") }