コピペコードで快適生活

明日使えるソースを自分のために

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")
}