Goの特徴 †
- Googleによって開発され2009年に発表されたプログラミング言語
- ネイティブコードにコンパイルされる
マルチプラットフォームで動作する - OSに依存しない、各OSの標準ライブラリに依存しない実行ファイルを生成する
- ガベージコレクションあり
- goroutineという並行処理の仕組みがあり、シンプルかつ効率的な処理が可能
- オブジェクト指向言語ではない
- メソッドという仕組みで、レシーバの型の関数を呼び出す
- タグという仕組みで、構造体にメタ情報を付加できる
interface{}
は全ての型のインターフェース
他の言語との差分で自分が気になった箇所のみ記述する。
言語仕様は、以下のURLに詳細にまとめられているのでそちらを参照のこと。
URL https://golang.org/ref/spec
エントリーポイント †
mainパッケージのmain関数がエントリーポイント
package main import "fmt" func main() { fmt.Printf("Hello, World\n") }
URL https://golang.org/ref/spec#Program_execution
go文 †
並行処理のための構文。
スレッドよりも小さな単位であるgoroutineが並行して動作する。
package main import ( "fmt" "time" ) func main() { termCh := make(chan struct{}) finCh := make(chan struct{}) go func(termCh, finCh chan struct{}) { defer func() { fmt.Println("go: close(finCh)") close(finCh) }() i := 0 for { fmt.Printf("go: running go routine = %d\n", i) i = i + 1 time.Sleep(1 * time.Second) select { case <-termCh: fmt.Println("go: received stop signal from main") return default: } } }(termCh, finCh) times := 0 for { time.Sleep(800 * time.Millisecond) if times > 3 { fmt.Println("main: send stop signal") close(termCh) break } times = times + 1 } <-finCh fmt.Println("main: done") }
URL https://golang.org/ref/spec#Go_statements
defer文 †
関数の終了時にdeferで指定された式を実行することができる。
リソースの解放処理などに使用できる。
package main import "fmt" func finalize() { fmt.Println("finalize") } func runDefer() { defer finalize() defer func() { fmt.Println("runDefer defer") }() fmt.Println("end runDefer()") } func main() { defer func() { fmt.Println("main defer") }() runDefer() fmt.Println("end main()") } // end runDefer() // runDefer defer // finalize // end main() // main defer
参考 https://golang.org/ref/spec#Defer_statements
iotaキーワード †
- ConstSpecのインデックス。
- const宣言ごとに0の値から始まる。
- constのブロック指定でない場合は、0になる。
package main import "fmt" const Out_block1 = iota // 0 (ConstSpec) const Out_block2 = iota // 0 (ConstSpec) const ( Blk1_a = iota // 0 (iota=0) (ConstSpec) Blk1_b = 0 // (iota=1) (ConstSpec) Blk1_c = iota // 2 (iota=2) (ConstSpec) ) const ( Blk2_a = iota // 0 Blk2_b = 0 // 0 Blk2_c = iota // 2 ) const ( Blk3_a1, Blk3_a2 = iota, iota // 0, 0 (ConstSpec) Blk3_b1, Blk3_b2 = iota, iota // 1, 1 (ConstSpec) ) func main() { fmt.Printf("Out_block1 = %d\n", Out_block1) fmt.Printf("Out_block2 = %d\n", Out_block2) fmt.Printf("Blk1_a = %d, Blk1_b = %d, Blk1_c = %d\n", Blk1_a, Blk1_b, Blk1_c) fmt.Printf("Blk2_a = %d, Blk2_b = %d, Blk2_c = %d\n", Blk2_a, Blk2_b, Blk2_c) fmt.Printf("Blk3_a1 = %d, Blk_a2 = %d\n", Blk3_a1, Blk3_a2) }
参考 https://golang.org/ref/spec#ConstSpec
init関数 †
init関数は、main関数の実行に先立って実行される。パッケージの初期化を目的にした特殊な関数である。通常ならば関数の重複はエラーとなるが、init関数は複数定義することができるようだ。
package main import "fmt" func init() { fmt.Println("init") } func init() { fmt.Println("init2") } func main() { fmt.Printf("Hello world\n") } // init // init2 // Hello world
URL https://golang.org/ref/spec#Package_initialization
new関数 †
指定した型のポインタ型を生成する。
package main import "fmt" type User struct { Id uint32 Name string Age uint16 } func main() { u := new(User) u.Id = 1 u.Name = "tarou" u.Age = 20 fmt.Printf("%#v\n", u) }
URL https://golang.org/pkg/builtin/#new
メソッド †
<レシーバ>.<メソッド>
という形式で呼び出すことができる。
package main import "fmt" type User struct { Id uint32 Name string Age uint16 } // メソッド func (user *User) ToString() string { return fmt.Sprintf("Id = %d, Name = %s, Age = %d", user.Id, user.Name, user.Age) } func main() { u := new(User) u.Id = 1 u.Name = "tarou" u.Age = 20 println(u.ToString()) } // Id = 1, Name = tarou, Age = 20
URL https://golang.org/ref/spec#Method_declarations
タグ †
構造体のフィールドにメタ情報を付加することができる。
package main import ( "encoding/json" "fmt" "reflect" ) type User struct { Id uint64 `json: "user_id"` Name string `json: "user_name"` } func main() { u := User{Id: 1, Name: "hello"} t := reflect.TypeOf(u) for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("%s => %v\n", field.Name, field.Tag) } js, _ := json.Marshal(u) fmt.Println(string(js)) } // Id => json: "user_id" // Name => json: "user_name" // {"Id":1,"Name":"hello"}
URL https://golang.org/ref/spec#Struct_types
インターフェース †
任意の型に対するメソッドの実装を規定することができる。
package main import ( "os" ) type MissingArgumentError struct { Code int Message string } func (err *MissingArgumentError) Error() string { return err.Message } func main() { if len(os.Args) < 2 { err := RaiseError("args are missing", 100) println(err.Error()) os.Exit(1) } println("ok") } func RaiseError(errmsg string, code int) error { return &MissingArgumentError{Message: errmsg, Code: code} }
URL https://golang.org/ref/spec#Interface_types
オブジェクト指向言語のような継承によるクラス階層のような機能は有していないが、クラスのように使用する記述をすると以下のような感じになる。
package main import "fmt" // プライベートメンバ type User struct { id int name string } // コンストラクタ func NewUser(id int, name string) *User { user := new(User) user.id = id user.name = name return user } // Getter func (user *User) Id() int { return user.id } // Getter func (user *User) Name() string { return user.name } func (user *User) String() string { return fmt.Sprintf("[id] %d, [name] %s", user.id, user.name) } func main() { u := NewUser(1, "admin") fmt.Println(u) }
参考 podmanのソースcontainer.do
多態 †
より実践的な例は以下の記事が参考になる。
URL インタフェースの実装パターン #golang - on https://qiita.com/tenntenn/items/eac962a49c56b2b15ee8
ただし、インターフェース定義が重複している場合はエラーとなることに注意。
// polmo.go type Handler interface { call(data string) string } type Handler2 interface { call(data string) string } type HandlerMain struct { Handler Handler2 // インターフェース定義の重複は許されない } // ... 省略 // 実行結果 # command-line-arguments ./polmo.go:30:10: ambiguous selector h.call
構造体メンバに識別子を持たせるのは可能(当然か...)。
// polmo.go type Handler interface { call(data string) string } type Handler2 interface { call(data string) string } type HandlerMain struct { h1 Handler h2 Handler2 }
ただ、上記のような形では、インターフェースのどちらにhandlerを設定するかを意識しなければならず分かりづらい。
handler.h1 = &MyHandler{} handler.h1.call("hoge") handler.h1 = &MyHandler2{} handler.h1.call("hoge") handler.h2 = &MyHandler{} handler.h2.call("hoge") handler.h2 = &MyHandler2{} handler.h2.call("hoge")
パッケージ †
- エントリーポイントはmainパッケージのmain関数
- packageは、Javaのようなディレクトリ構造と1対1とは限らない
- importでは、packageのnameが名前空間の識別子
補足 go build、go run時について
// --------------------------- // other.go // --------------------------- package main var otherName = "otherName" // --------------------------- // main.go // --------------------------- package main func main() { // go build , go run *.go println(otherName) }
上記でotherName
を表示するには、go build
、go run *.go
とする必要あり。
go run main.go
、go build main.go
とするとエラーとなる。
# command-line-arguments ./main.go:5:13: undefined: otherName
Vendoring †
go 1.6でvendor、go 1.12でmoduleという機能がサポートされている。
https://golang.org/cmd/go/#hdr-GOPATH_and_Modules
// main.go package main import ( "fmt" "rsc.io/quote" ) func main() { fmt.Println(quote.Opt()) }
$ export GO111MODULE=on $ go mod init example.com/hoge/hello $ ls go.mod main.go $ go mod vendor $ ls go.mod go.sum main.go vendor $ tree -d vendor vendor ├── golang.org │ └── x │ └── text │ ├── internal │ │ └── tag │ └── language └── rsc.io ├── quote └── sampler $ go build $ ls go.mod go.sum hello main.go vendor
参考リンク †
- https://golang.org
- https://github.com/golang/go
- https://golang.org/doc/articles/wiki/
- https://go言語.com/doc/- on https://go言語.com/doc/
実装例、TIPS †
goroutineのパターンが分かりやすくまとめられている。
数多くの実装例が掲載されている。
文字列コピーの効率的な方法
- golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる - on https://qiita.com/mattn/items/176459728ff4f854b165
初学者に分かりやすく役立つ情報 †
- A Tour of Go - on https://go-tour-jp.appspot.com/list
- Welcome to a tour of Go -on https://go-tour-jp.appspot.com/list
- Goならわかるシステムプログラミング - on http://ascii.jp/elem/000/001/235/1235262/
- Go入門 - on https://www.slideshare.net/takuyaueda967/2016-go
- Go言語の初心者が見ると幸せになれる場所 #golang - on https://qiita.com/tenntenn/items/0e33a4959250d1a55045
- スターティングGo言語 (CodeZine BOOKS) - on https://www.amazon.co.jp/dp/4798142417/