Goの特徴

他の言語との差分で自分が気になった箇所のみ記述する。
言語仕様は、以下の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キーワード

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

パッケージ

補足 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 buildgo run *.goとする必要あり。
go run main.gogo 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

参考リンク

実装例、TIPS

goroutineのパターンが分かりやすくまとめられている。

数多くの実装例が掲載されている。

文字列コピーの効率的な方法

初学者に分かりやすく役立つ情報

goroutineについて


トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP