- 真偽値 bool
- 文字列 string
- 整数 int(8, 16, 32, 64)
- 符号なし整数 uint(8, 16, 32 64)
- バイト byte (uint8 の別名)
- 文字 rune (Unicodeのコードポイントを指す。int32 の別名)
- 浮動小数点 float32(64)
- 複素数 complex64(128)
int や uintが示すビット数は、システムに依存する。 32bitの場合は32、64bitの場合は64。
- 宣言は変数名と型名の順: var x int
- 型は省略可
- 初期化
var x int = 100- 省略記法
x := 100 - 初期値のリテラル値から変数の型が決まる。
- 省略記法
- 初期化しなかった場合、型に応じて初期値(ゼロ値)が与えられる。
- 数値型(int,floatなど): 0
- bool: false
- string: ""(空文字)
- 型変換: 値
vを型Tに変換する場合T(v)
- 真偽値:
true,false - 文字列:
"(ダブルクォート)で囲んだ文字列"This is a String"
- 整数:
123 - 浮動小数:
0.1234 - 複素数:
1 + 2.3i - 配列:
[3]T{ t1, t2, t3 }- ここで
Tは型、t1、t2、t3は型Tの値
- ここで
- スライス:
[]T{ t1, t2, t3 }
var p *int
x := 100
p = &x
*p = 200- int型のメモリアドレスを指すポインタ変数を定義する際に、型名の前に
*をつける。 - 変数のメモリアドレスを参照する際に、変数名の前に
&をつける。 - ポインタ変数が指すメモリアドレスの変数を参照する際に、ポインタ変数の前に
*をつける。
constを使って宣言する。const X = 100
func add(x int, y int) int {
return x + y
}- 戻り値の型を指定する必要がある。
return x, yのよう複数の値を返すことも可能
func add(x int, y int) (sum int) {
sum = x + y
return
}- 関数の戻り値には名前をつけることができ、関数内で参照することができる。
- その際、
returnに引数を与えなかった場合の戻り値としてsumの値が用いられる。
- その際、
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
}Cのfor文と同様に、
- 初期化
- 条件式
- 後処理
初期化、後処理は省略することができ、さらにセミコロンを略すことで、while文と同じになる。
sum := 1
for sum < 1000 {
sum += 10
}を記述する。 条件をなくすことで、無限ループを表現できる。
if x < 0 {
fmt.Println('Negative')
} else if x > 0 {
fmt.Println('Positive')
} else {
fmt.Println('Zero')
}goのif文はbooleanでしか条件を判断しないので、ゼロ値などで条件式に使うことはできない。
x := "foo"
switch x {
case "foo":
fmt.Println("foo!")
case "bar":
fmt.Println("bar!")
default:
fmt.Println("boo!")
}goのswitch文は、breakがない。
マッチするcaseで実行された以降のcase文は実行されない。
上の例では、foo!と出力されるのみ。
func main() {
defer fmt.Println("World")
fmt.Println("Hello,")
}関数の終了後に事項される関数を登録する。 deferに登録された関数に与えられる引数は、deferを実行した時点で評価される。 deferを複数回実行した時、最後に登録された関数から順に実行される(LIFO)。 IOのクローズや例外処理で利用される。
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 100
v.Y = 200
}var a [10]intGoの配列は固定長。
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4] # <= Slice, [ 3, 5, 7 ]- スライスは配列の部分列で、スライスの要素を変更すると、下の配列の要素も変更される。また、その逆も。
- スライスリテラル:
[]bool{true, false, true} - 配列
var a [10]intがある時、スライス式a[low:high]のそれぞれを省略した場合、配列の先頭および末尾として扱われる。下記の4つの式は等価。a[0:10]a[:10]a[0:]a[:]
make,appendを使い、動的な配列(スライス)を生成し、要素を追加することができる。
PHPの連想配列、RubyのHashに相当する。
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
// リテラル
m = map[string]Vertex {
"Foo": Vertex{},
"Bar": Vertex{}
}
m["Hoge"] = Vertex{}
delete(m, "Hoge")elem, ok = m["Bar"] のように2つの値を返す。
ここで、ok に有無を表すboolean型の値が入る。
if elem, ok := m["Bar"]; ok {
fmt.Println("Bar's value is ", elem)
} else {
fmt.Println("No Bar's value")
}func compute(fn func(float64) float64) float64 {
return fn(3)
}
func main() {
sqr := func(x float64) float64 {
return x * x
}
fmt.Println(compute(sqr))
}- 関数はクロージャーであり、それ自体を値として利用することができる。
- その時の型の定義は、上記の例の
func(float64) float64のように、引数と戻り値の型を指定する。
- その時の型の定義は、上記の例の
A Tour of Go中のExerciseに出てくるFibonatti関数を返す関数は以下のように書ける。
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
sequence := map[int]int{
0: 0,
1: 1,
}
n := 0
return func() int {
defer func() { n++ }()
if v, ok := sequence[n]; ok {
return v
}
sequence[n] = sequence[n-1] + sequence[n-2]
return sequence[n]
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}Goにはクラスは存在しないが、構造体にメソッドを定義することができる。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Sqrt() float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
func (v *Vertex) setX(x float64) float64 {
v.X = x
return x
}
func main() {
v := Vertex{4, 29}
fmt.Println(v.Sqrt())
p := &Vertex{11, 29}
p.setX(10)
fmt.Println(p.Sqrt())
}- メソッドの定義は、関数の定義にレシーバーとなる構造体(もしくは構造体のポインタ)を指定することでできる。
- 上の例の
pのように、構造体のポインタであれば、構造体自体に定義されたメソッドを呼ぶことが出来る。 - Goの関数の引数は値渡しであり、メソッドのレシーバーもまた引数として扱われる。
- 後述するインターフェースの定義によって、構造体もしくは構造体のポインタのどちらかにレシーバーの定義を統一することが推奨される。
- 上の例の
setXのように構造体に変更を与えるメソッドを定義する場合は、ポインタでないと出来ない。 - また、フィールドを多く持つ構造体の場合は、値渡しのためのコピーにコストがかかる。
- 上の例の
Goのインターフェースは、指定されたメソッドを持つ
type I interface {
M() int
}
type A struct {
X int
}
func (a A) X() int {
return a.X
}
type B struct {
}
func (b *B) X() int {
return 10
}
func main() {
var i I
i = A{100}
i = &B
// i = B fails
}上の例では、インターフェースIは、「メソッドXを持つ型」として定義されている。
インターフェースIの変数iに、構造体Aの値は代入できる。
また、構造体BのポインタにメソッドXが定義されているので、構造体Bのポインタ&Bはiに代入できる。
しかし、構造体Bの値自体はiに代入できない。
インターフェースは変数の定義時にのみ指定できる。
そのため、実際の値が入るまではnilを取り、
また、構造体のポインタにメソッドを定義する場合、値を持たないままメソッドを呼ぶことができるため、内部でnilを適切に取り扱う必要がある。
package main
import "fmt"
type I interface {
M()
}
type S struct {
Str string
}
func (s *S) M() {
if s == nil {
fmt.Println("Nil")
return
}
fmt.Println("Str is ", s.Str)
}
func main() {
var i I
var s *S
i = s
i.M()
i = &S{"Hello"}
i.M()
}interface{} は、全ての型と構造体を表すインターフェースとして使える。
しかし、このインターフェースで定義されているべきメソッドは存在していないので、代入することしかできない。
var i interface{}
i = 100
i = "hoge"インターフェースの変数の値が、実際にどの型なのかを調べる方法として、i.(T)を使う。
v, ok := i.(T)iの実際の型がTの時、okはtrueであり、そうでないときはfalseが入る。
いきなり、i.(T)を参照し、Tでなかった場合は実行時エラーになる。
また、別の方法として、型スイッチがある。
switch v := i.(type) {
case int: fmt.Println("This is int")
case T: fmt.Println("This is T")
default: fmt.Println("Other")
}fmtパッケージが提供するフォーマット出力に対応させるためのインターフェースとしてStringerインターフェースが定義されている。
fmt.Printlnなどは、与えられた引数の構造体のStringメソッドから戻ってきた文字列を出力する(JavaのtoStringメソッドと同様)。
package main
import "fmt"
type Vertex struct {
X, Y int
}
func (v Vertex) String() string {
return fmt.Sprintf("(X, Y) = (%v, %v)", v.X, v.Y)
}
func main() {
v := Vertex{7, 29}
fmt.Println(v)
}Goの特徴として、並行プログラミングを比較的容易に実装できるためのgoroutineやそれをサポートするパッケージがある。
go f(x) のように、関数f�を呼ぶことでgoroutineとして関数fを実行できる。
呼び出し元の処理とは別の処理として並行して実行される。
関数fの引数xは、このgo文の時点で評価された値が与えられる。
goroutineとのデータの送受信�にチャネルを使う。
package main
import "fmt"
func Names(c chan string) {
names := []string{"Go", "JavaScript", "PHP"}
for _, name := range names {
c <- name
}
close(c)
}
func main() {
ch := make(chan string)
go Names(ch)
for {
name, ok := <-ch
if ok {
fmt.Println(name)
} else {
break
}
}
ch = make(chan string)
go Names(ch)
for name := range ch {
fmt.Println(name)
}
}- チャネルの生成は、
make関数で行う。make(chan string, 100)のようにチャネルのサイズ(バッファ)を指定できる。
- チャネルの型は、
chan stringのようにチャネルで送受信を行う型を指定する。 - 送信:
ch <- v - 受信:
v = <-ch - チャネルの送受信の評価で処理は一旦ブロックされ、値をチャネルから受信(または送信)できるようになったら時点で再開される。
- チャネルは送信側(上の例だと関数
Names)のみチャネルをクローズすることができる。- 受信側では、
name, ok := <-chのようにチャネルが閉じているかどうかを真偽値として受け取ることができる。 - また
range chのようにrangeを使う場合は、送信側がcloseを実行しないと実行時エラーになる。
- 受信側では、
複数のチャネルを利用する場合に、selectを使って制御できる。
package main
import (
"fmt"
"time"
)
func Greeting(names chan string, control chan bool) {
for {
select {
case name := <-names:
fmt.Printf("%v DEATH!\n", name)
case <-control:
break
default:
fmt.Println("... Waiting")
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
names := []string{"YUIMETAL", "MOAMETAL", "SU-METAL"}
ch := make(chan string)
c := make(chan bool)
go Greeting(ch, c)
for _, name := range names {
ch <- name
time.Sleep(4 * time.Second)
}
c <- true
}Go v1.12から標準で依存管理の仕組みとしてGo Modulesが導入された。 モジュールとはGoパッケージの集まりであり、GitHubなどソースコード管理ツールから公開配布される。
開発しているモジュールが依存するモジュールのパスを記述する。 Go Modulesの依存解決がセマンティックバージョニングに基づいているため、モジュールのバージョニングもこれに従うことが求められる。
go.modファイルの例を以下に示す。
module M
require (
A v1
B v1.0.0
C v1.2.3
)
exclude C v1.2.3moduleはモジュール名とバージョンを指定する。
requireは、モジュールMが依存するモジュールのパスとバージョンを指定する。
excludeは、requireで指定された依存モジュールの中で、使用しないバージョンを指定する。
上の例の場合、
モジュールMは、以下のモジュールに依存している。
モジュールA v1.0.0以降
モジュールB v1.0.0以降
モジュールC v1.2.3以降、且つv1.2.3以外
実際にどのバージョンになるかは、go modコマンドを実行し、モジュールを取得する時点で上記の条件を満たすものに決まる。
開発するGoモジュールのワークスペースに移動し、go mod initコマンドを実行する。
すると、go.modファイルが生成され、依存関係を記述するための準備が完了する。
go.modに依存関係を記述した後、go.modと同じディレクトリ上へ移動し、go mod downloadを実行する。
すると、go.modに基づいて依存モジュールの依存解決が行われ、該当のバージョンのダウンロードが実施される。
実際にどのバージョンをダウンロードしたのかは、go.sumに記録される。
- Go Modulesの概要とGo1.12に含まれるModulesに関する変更 #golangjp #go112party - My External Storage
- モジュール対応モードへの移行を検討する — プログラミング言語 Go | text.Baldanders.info
- go - The Go Programming Language
- research!rsc: Minimal Version Selection (Go & Versioning, Part 4)
- 言語
- IDE
- Web framework
- Rails、Sinatra、Rackみたいなものを探してみる。
- Qiitaで探して、軽く作ってみる。https://qiita.com/loftkun/items/1a9951d1864bebdc51e1
- パッケージマネージャ
- bundler, gem みたいなもの探す。 -> Go modules
- パッケージ配布
- rubygems.org みたいなものを探す。
- テストフレームワーク
- test-unit 、RSpec みたいなもの
- デプロイ
- デプロイの方法
- ホスティングサービス
- とりあえず動かせる場所としてよく使われてそうなもの(Heroku?