- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2018-12-04T14:28:26+00:00","default:haikikyou","haikikyou")
[[HaikikyouのIT関連技術メモ]]
#author("2020-05-05T04:24:51+00:00","default:haikikyou","haikikyou")
#contents
* Go [#xd982f04]
* Goの特徴 [#i5aac11b]
- Googleによって開発され2009年に発表されたプログラミング言語
- ネイティブコードにコンパイルされる
- マルチプラットフォームで動作する
- OSに依存しない、各OSの標準ライブラリに依存しない実行ファイルを生成
- ネイティブコードにコンパイルされる~
マルチプラットフォームで動作する
- OSに依存しない、各OSの標準ライブラリに依存しない実行ファイルを生成する
- ガベージコレクションあり
- goroutineという並行処理の仕組みがあり、シンプルかつ効率的な処理が可能
- ''オブジェクト指向言語ではない''
- メソッドという仕組みで、レシーバの型の関数を呼び出す
- タグという仕組みで、構造体にメタ情報を付加できる
- &code(){interface{}};は全ての型のインターフェース
他の言語との差分で自分が気になった箇所のみ記述する。~
言語仕様は、以下のURLに詳細にまとめられているのでそちらを参照のこと。
&label(link){URL}; https://golang.org/ref/spec
** エントリーポイント [#c580f965]
mainパッケージのmain関数がエントリーポイント
#geshi(go){{{
package main
import "fmt"
func main() {
fmt.Printf("Hello, World\n")
}
}}}
&label(link){URL}; https://golang.org/ref/spec#Program_execution
** go文 [#g0d8b9bc]
並行処理のための構文。~
スレッドよりも小さな単位であるgoroutineが並行して動作する。
#geshi(go){{{
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")
}
}}}
&label(link){URL}; https://golang.org/ref/spec#Go_statements
**defer文 [#rd04604a]
関数の終了時にdeferで指定された式を実行することができる。~
リソースの解放処理などに使用できる。
#geshi(go){{{
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
}}}
&label(warn){参考}; https://golang.org/ref/spec#Defer_statements
** iotaキーワード [#z6b7b9fc]
- ConstSpecのインデックス。~
- const宣言ごとに0の値から始まる。
- constのブロック指定でない場合は、0になる。
#geshi(go){{{
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)
}
}}}
&label(warn){参考}; https://golang.org/ref/spec#ConstSpec
** init関数 [#kf0dcdc8]
init関数は、main関数の実行に先立って実行される。パッケージの初期化を目的にした特殊な関数である。通常ならば関数の重複はエラーとなるが、init関数は複数定義することができるようだ。
#geshi(go){{{
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
}}}
&label(link){URL}; https://golang.org/ref/spec#Package_initialization
** new関数 [#dc369db8]
指定した型のポインタ型を生成する。
#geshi(go){{{
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)
}
}}}
&label(link){URL}; https://golang.org/pkg/builtin/#new
** メソッド [#y3514723]
&code(){<レシーバ>.<メソッド>};という形式で呼び出すことができる。
#geshi(go){{{
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
}}}
&label(link){URL}; https://golang.org/ref/spec#Method_declarations
** タグ [#fee8af3d]
構造体のフィールドにメタ情報を付加することができる。
#geshi(go){{{
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"}
}}}
&label(link){URL}; https://golang.org/ref/spec#Struct_types
** インターフェース [#tbcc433c]
任意の型に対するメソッドの実装を規定することができる。~
#geshi(go){{{
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}
}
}}}
&label(link){URL}; https://golang.org/ref/spec#Interface_types
オブジェクト指向言語のような継承によるクラス階層のような機能は有していないが、クラスのように使用する記述をすると以下のような感じになる。
#geshi(go){{{
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)
}
}}}
&label(warn){参考}; ''podmanのソースcontainer.do'' ~
- https://github.com/containers/libpod/blob/master/libpod/container.go
*** 多態 [#y8d93411]
より実践的な例は以下の記事が参考になる。
&label(link){URL}; [[インタフェースの実装パターン #golang>https://qiita.com/tenntenn/items/eac962a49c56b2b15ee8]] - &size(11){&color(gray){on https://qiita.com/tenntenn/items/eac962a49c56b2b15ee8};};
ただし、インターフェース定義が重複している場合はエラーとなることに注意。
#geshi(go){{{
// 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
}}}
構造体メンバに識別子を持たせるのは可能(当然か...)。
#geshi(go){{{
// polmo.go
type Handler interface {
call(data string) string
}
type Handler2 interface {
call(data string) string
}
type HandlerMain struct {
h1 Handler
h2 Handler2
}
}}}
ただ、上記のような形では、インターフェースのどちらにhandlerを設定するかを意識しなければならず分かりづらい。~
#geshi(go){{{
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")
}}}
** パッケージ [#g39db491]
- エントリーポイントはmainパッケージのmain関数
- packageは、Javaのようなディレクトリ構造と1対1とは限らない
- importでは、packageのnameが名前空間の識別子
&label(info){補足}; go build、go run時について
#geshi(go){{{
// ---------------------------
// other.go
// ---------------------------
package main
var otherName = "otherName"
// ---------------------------
// main.go
// ---------------------------
package main
func main() {
// go build , go run *.go
println(otherName)
}
}}}
上記で&code(){otherName};を表示するには、&code(){go build};、&code(){go run *.go};とする必要あり。~
&code(){go run main.go};、&code(){go build main.go};とするとエラーとなる。
#geshi{{{
# command-line-arguments
./main.go:5:13: undefined: otherName
}}}
* Vendoring [#ja0e7612]
go 1.6でvendor、go 1.12でmoduleという機能がサポートされている。~
https://golang.org/cmd/go/#hdr-GOPATH_and_Modules
#geshi(go){{{
// main.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Opt())
}
}}}
#geshi(bash){{{
$ 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
}}}
* 参考リンク [#d0f91440]
- https://golang.org
- https://github.com/golang/go
- https://golang.org/doc/articles/wiki/
- [[https://go言語.com/doc/>https://xn--go-hh0g6u.com/]]- &size(11){&color(gray){on https://go言語.com/doc/};};
** 実装例、TIPS [#k76a1a60]
goroutineのパターンが分かりやすくまとめられている。
- http://jxck.hatenablog.com/entry/20130414/1365960707
数多くの実装例が掲載されている。
- [[Go by Example>https://gobyexample.com]] - &size(11){&color(gray){on https://gobyexample.com};};
文字列コピーの効率的な方法
- [[golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる>https://qiita.com/mattn/items/176459728ff4f854b165]] - &size(11){&color(gray){on https://qiita.com/mattn/items/176459728ff4f854b165};};
** 初学者に分かりやすく役立つ情報 [#x5950fd2]
- [[A Tour of Go>https://go-tour-jp.appspot.com/list]] - &size(11){&color(gray){on https://go-tour-jp.appspot.com/list};};
- [[Welcome to a tour of Go>https://go-tour-jp.appspot.com/list]] -&size(11){&color(gray){on https://go-tour-jp.appspot.com/list};};
- [[Goならわかるシステムプログラミング>http://ascii.jp/elem/000/001/235/1235262/]] - &size(11){&color(gray){on http://ascii.jp/elem/000/001/235/1235262/};};
- [[Go入門>https://www.slideshare.net/takuyaueda967/2016-go]] - &size(11){&color(gray){on https://www.slideshare.net/takuyaueda967/2016-go};};
- [[Go言語の初心者が見ると幸せになれる場所 #golang>https://qiita.com/tenntenn/items/0e33a4959250d1a55045]] - &size(11){&color(gray){on https://qiita.com/tenntenn/items/0e33a4959250d1a55045};};
- [[スターティングGo言語 (CodeZine BOOKS)>https://www.amazon.co.jp/dp/4798142417/]] - &size(11){&color(gray){on https://www.amazon.co.jp/dp/4798142417/};};