Golang
Packages, Valiables and Functions
Packages
- Go のプログラムはパッケージで成り立っている
main
パッケージのmain
関数からプログラムが始まる- パッケージ名はインポートパスの最後の要素と同じ名前である(
math/rand
ならrand
) - 一つのフォルダには一つのパッケージしか含められない(例外は
フォルダ名_test
)。通常はフォルダ名をパッケージ名にする。
Imports
// bad
import "fmt"
import anotherName "math"
import _ "io" // 初期化処理だけ使いたい場合
// good (factored import)
import (
"fmt"
anotherName "math"
import _ "io"
)
Exported names
- 大文字で始まる名前がエクスポートされる
- パッケージをインポートしたとき、エクスポートされた名前のみ参照できる
Functions
- 各引数の後に型を記載する
()
の後に返値の型を記載する
func add(x int, y int) int {
return x + y
}
// 連続する引数が同じタイプなら省略できる
func add(x, y int) int {
return x + y
}
Multiple results
ファンクションは複数の値を返すことができる。
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b) // => world hello
}
Named return values
- 戻り値に名前をつけると、関数の最初で定義した変数として扱われる
- 名前付き戻り値を使い、かつ return に何も書かなかった場合、自動的に名前付き戻り値がリターンされる(naked return)。ただし、長い関数では読みやすさの点から明示的に記載すること。
- 戻り値の意味を表す名前をつけるよう心がけること
func split(sum int) (x, y int) {
x = sum / 2
y = sum - x
return
}
func main() {
a, b := split(20)
fmt.Println(a, b)
}
値渡しと参照渡し
- 関数に引数を渡した場合、値はコピーされる。つまり、呼び出し元の変数とは別物である。
- 下記の場合、
i
はmyInt
のコピーであり、p
はmyPointer
のコピーである。
func test(i int, p *int) {}
func main() {
myInt := 123
myPointer := &myInt
test(myInt, myPointer)
}
可変長パラメータ
- 最後の引数に限って、
...
をつけることで可変長のパラメータとして受け取れる - 受け取ったパラメータはスライスとして扱われる。
// c の型は []string になる
func someFunc(a int, b int, c ...string){}
関数リテラル
- 匿名関数(クロージャ)を作るときに使う
- 記法としては、通常の関数宣言から名前を削除するだけ
- リテラルの外側の変数にアクセス可能
- 名前がないので、関数を呼び出す際には、IIFE にするか、変数に代入して後から呼び出す。
// 関数リテラル
func (a int, b int) int {}
関数型
- 関数を変数に代入するときの型として扱うためのもの
- 引数名は省略可能であり、通常は書かない
// 関数型の宣言
var a func(int, int) int
// 実装
a = func(a int, b int) int {}
Variables
var
で変数を宣言できる。
// 複数の同じ型の変数はまとめて宣言できる。
var a, b, c bool
func main() {
var d int
fmt.Println(a, b, c, d) // => false false false 0
}
Initializers
宣言時に内容を設定する場合は、型定義を省略できる。
var a, b, c = 1, true, "no!"
// or factored style
var (
a = 1
b = true
c = no
)
fmt.Println(a, b, c) // => 1 true "no!"
Short variable declarations
- ファンクションの中であれば、
:=
を使うことでvar
を省略できる。 - ファンクションの外では、すべての Statement は
var
,func
,import
などのキーワードから始まる必要があるため、省略形は使えない。
func someFunc() {
a, b, c := 1, true, "no!"
fmt.Println(a, b, c) // => 1 true "no!"
}
Basic types
int
,uint
,uintptr
は、32bit システムでは 32bit、64bit システムでは 64bit である。- 特に理由がなければ、サイズ指定やアンサインドは使わず、
int
を使うこと。
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr (u = unsigned)
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
Zero values
明示的に初期値を設定しない限り、変数には「Zero Values」がセットされる
type | value |
---|---|
数値 | 0 |
boolean | false |
文字列 | "" (空文字列) |
struct | 各フィールドがゼロ値の struct |
配列、スライス | 各要素がゼロ値の配 列 |
マップ | 空のマップ? |
その他(ポインタ、関数型など) | nil |
var a int // => 0
var b float64 // => 0
var c bool // => false
var d string // => ""
Type conversions
Type(value)
の形で型変換を行える。
var i float64 = float64(123)
j := uint(456)
Type inference
- 明示的に型を指定しなかった場合は、右辺の値から型推論される。
- 数値の場合は、必要な精度により、適切な方が自動で選択される。
var i = 1 // int
j := 1 // int
a := 42 // int
b := 3.142 // float64
c := 0.867 + 0.5i // complex128
Constants
- 定数には文字列、ブーリアン、数値を使うことができる。
:=
記法は使えない
const Pi float64 = 3.14
const (
A int = 123
B string = "hello"
)
Numeric Constants
- 数値定数は untyped(型を持たない)にできる
- untyped な数値定数は高精度である
- untyped な数値定数は、実行時の文脈で型が決まる
// タイプを指定していない
const Big = 1 << 100
func needFloat(x float64) float64 { return x * 0.1 }
func main() {
fmt.Println(needFloat(Big)) // ここで初めて型が決まる(float64)
}
Literals
// 論理値リテラル
true, false
// 整数リテラル
8, 0x23
// 浮動小数点リテラル
0.0, 12.5, 12e5
// 虚数リテラル
0i, -123.45i
// 文字(ルーン)リテラル
'a', 'b'
// 文字列リテラル
"エスケープが\n解釈される"
`エスケープが\n無視されるが、
改行を入れることができる`
// 関数リテラル
func (string, int) []string {}
Flow control statements
for
- 3 つの要素を記載できる
- init statement:初回のみ実行。ここで定義した変数は for の中でのみ有効。
- condition expression:毎回繰り返しの冒頭で評価
- post statement:毎回繰り返しの最後で実行
()
は不要
for i := 0; i < 10; i++ { }
// 一部を省略できる
for ; sum < 1000; i++ { }
// init statementとpost statementを両方省略するときは
// セミコロンを省略できる
for sum < 1000 { }
// 何も要素を記載しなければwhileとして使える
for { }
range
range()
を使うと便利にループ処理できる。
for index, value := range my_slice {}
// 省略するときは`_`にするか、削除する
for index := range slice {}
for _, value := range slice {}
range で使える型は下記の通り
type | 1st value | 2nd value |
---|---|---|
Array, Slice | インデックス | 値 |
string | バイトインデックス | unicode コードポイント(rune 型) |
map | キー | 値 |
channel | 値 | - |
if
()
は不要- init statement を記載することができる。ここで定義した変数は if の中でのみ有効。
if x < 0 {
} else {
}
if x := 100; x < 0 {
// 実行されない
} else {
// 実行される
}
switch
- init statement を記載することができる。ここで定義した変数は switch の中でのみ有効。
- 上から順に評価され、最初に当てはまった1つのケースだけ実行される。
break
は不要。
switch a := 1; a {
case 1:
fmt.Println("number is one")
case 2:
fmt.Println("number is two")
}
- 他の言語と異なり、Go では
case
節に式を書くことができる。 - 条件 式を省略すると、
switch true
を指定したのと同じことになる。 - 上記を組み合わせることで、
if-elseif-else
を簡略に書くことができる
switch {
case b == 1:
fmt.Println("number is one")
case b == 2:
fmt.Println("number is two")
}
defer
引数の評価のみ即時に行い、自身を包含するファンクションがreturn
するまで待ってから実行する。
func main() {
defer fmt.Println("world")
fmt.Println("hello")
} // => "hello world"
defer statement はスタックに追加されていくため、最終的には逆順で実行される。
for i := 0; i < 10; i++ {
defer fmt.Println(i) // 9 8 7 6 5 4 3 2 1 0
}
More types
Pointers
ポインタ=「変数の内容が存在するメモリ上のアドレス」を保持する変数
*タイプ
でポインタを宣言できる
var pointer *int
&変数
でメモリアドレスを取得できる
i := 123
pointer = &i
*ポインタ
で「そのメモリアドレスに存在する内容」を参照できる。これを、dereferencing、indirecting、値参照、間接参照などという。
fmt.Println(*pointer) // => 123
*pointer = 456
fmt.Println(*pointer) // => 456
メモリ領域の確保
new()
を使うことで、ポインタに明示的にメモリ領域を割り当てることができる。確保された領域にはゼロ値が代入される。
var i *int = new(int) // i == メモリアドレス, *i == 0
*i := 123 // => ok
var i *int // i == nil
*i := 123 // => panic
変数とポインタの違い
ポインタは、「メモリアドレス型の変数」と考えるべし(ただし、特別な挙動を持つ)
その証拠に、変数・ポインタの、どちらも「値」と「メモリアドレス」を持つ。
値には無印で、メモリアドレスには&
を頭につけることでアクセスできる。
i := 1234
p := &i
fmt.Println(i, &i) // => 1234 0xc000018090
fmt.Println(p, &p) // => 0xc000018090 0xc000006028
- 特別な挙動
- ポインタだけが持つ特別な挙動が、値参照である。
*
を頭につけることで、値として持っているメモリアドレス内に存在する値にアクセスできる。
- 代入時等の振る舞い
- 代入をすれば、当然ながら「値」がコピーされる
- 変数なら、「値」は数値や文字列なので、それらがコピーされる
- ポインタなら、「値」はアドレスなので、アドレスがコピーされる
- 根本的には、変数やポインタが実際に格納している本質は、アドレスとその解釈方法である
- 解釈方法 = 型 = 数値として読む、文字として読む、アドレスとして読む、など
Structs
struct
型は、複数フィールドから成る型である- struct の値にはドットでアクセスできる
var x struct {
// 先頭を大文字にすると外部パッケージからもアクセスできるようになる
i int
f float32
s string
}
x.i = 1
x.f = 1.1
x.s = "hello"
- 構造体型を毎回手書きするのは面倒なので、名前のついた Struct 型として宣言することができる
- 名前をつけると、後述の Struct Literals を使うことで簡単に構造体を生成できる。
type Car struct {
Name string
Age int
}
car := Car{"bmw", 15}
Zero Values
Struct の各フィールドには Zero Values がセットされる
type MyType struct {
MyString string
MyInt int
MyFloat float64
}
func main() {
var t MyType
fmt.Printf("%T %v\n", t.MyString, t.MyString) // => ''
fmt.Printf("%T %v\n", t.MyInt, t.MyInt) // => 0
fmt.Printf("%T %v\n", t.MyFloat, t.MyFloat) // => 0
}
Pointers to structs
- struct のポインタで値参照する場合は、単純に
pointer.Name
のようにすればできる。 *(pointer).Name
などとする必要はない
car := Car{"bmw", 15}
pointer := &car
fmt.Println(pointer.Name) // => {bmw 15}
Struct Literals
全てのフィールド値を順番に記述する方法
car := Car{"bmw", 15}
name: value
構文を使って、一部の値だけを記述する方法(指定しなかったフィールドには zero values が設定される)
car := Car{Name: "bmw"} // Age:0 implicitly
car := Car{} // Name:"" and Age:0 implicitly
&
を頭につけると、作成された struct へのポインタを取得できる。
p := &Car{} // has type *Car
改行するときは、最後のトレーリングカンマが必須なので注意。
car := Car{
Name: "bmw",
Age: 18, // このカンマが無いとエラーになる
}
匿名フィールド
フィールド名を省略すると、型名がフィールド名になる。こうしたフィールドを「匿名フィールド」という。
type someStruct struct {
int // フィールド名は`int`
uini64 // フィールド名は`uint64`
*byte // フィールド名は`byte`
}
埋め込み
- 子 struct に親 struct を匿名フィールドとして指定することで、親の持つフィールドとメソッドを子で利用可能になる。これを「埋め込み」という。
- 親のフィ ールドやメソッドを使うときは単に
child.doSomething()
のようにすればよい。ただし、複数の struct を埋め込んで名前が重複したときは、明示的にchild.parentStruct.doSomething()
のように指定する必要がある。 - 埋め込みを使うと、継承に似たことができる。
type parentStruct struct {
i int
}
func (e parentStruct) doSomething() {
fmt.Println("hello")
}
type childStruct struct {
parentStruct
}
func main() {
var child childStruct
child.doSomething() // => "hello"
//child.parentStruct.doSomething()としなくていい!
fmt.Println(child.i) // => 0
//fmt.Println(child.parentStruct.i)としなくていい!
}
Arrays
[n]T
で、長さ n で型が T の配列を作成できる。配列の長さも型の定義に含まれる。長さは後から変更できない。- 配列の作成は配列リテラルを使う。
var a [3]int // 各要素はゼロ値
a[0] = 100
a[1] = 200
a[2] = 300
// 配列リテラル
b := [6]int{} // 各要素はゼロ値
c := [6]int{2, 3, 5, 7, 11, 13}
// ...を使うと、長さが自動で設定される。下記の例だと3になる。
d := [...]int{2, 3, 5}
Array 型の変数は、配列全体を表現している。C 言語 のように配列の先頭を指すポインタではない。このため、代入を行うと配列全体がコピーされる。
a := [3]int{1, 2, 3}
b := a
b[0] = 999
fmt.Printf("%v", a) // [1 2 3]
fmt.Printf("%v", b) // [999 2 3]
Slices
詳細はこのブログを参照
- スライスは Array の上に構築されている。Go では配列よりもスライスをよく使う。
- スタートは後ろにずらすことができる(前にずらすことはできない)
- エンドは前後にずらすことができる
someArray[1:4]
で、要素 1 から要素 3 までを含むスライスを作成できる
[3]int // 配列型
[]int // スライス型
arrayOrSlice[MIN:MAX] // スライス式
array := [6]int{1, 2, 3, 4, 5, 6}
var slice []int // スライス型の変数を容易
slice = array[1:4] // スライス式でスライスを作成して代入
fmt.Println(slice) // => 2,3,4
Slices are like references
- スライスは配列への参照のようなもの
- スライスはどんなデータも格納しておらず、単に元の配列の一部分を指し示しているだけ
- スライスの要素を変更すると、その元となる配列の対応する要素が変更される
- 同じ元となる配列を共有しているスライスは、お互いの変更が反映される
array := [6]int{1, 2, 3, 4, 5, 6}
slice1 := array[0:3]
slice2 := array[0:3]
slice1[0] = 123
fmt.Println(array) // => 123, 2, 3, 4, 5, 6
fmt.Println(slice1) // => 123, 2, 3
fmt.Println(slice2) // => 123, 2, 3
Creating a slice with literals
- いきなりスライスを作成するリテラル
- 長さを指定しない配列リテラルのような書き方をする
- 下記のスライスは、上段の配列と全く同じものを作成した上で、その配列を参照するスライスを返す。
// array literal
[3]bool{true, false, false}
// slice literal
[]bool{true, false, false}
Creating a slice with make
make(スライス, length, capacity)
関数を使ってスライスを作成することで、動的にサイズ変更できる配列を取得できる。- 配列の各要素は Zero Values で埋められる
a := make([]int, 5) // [0:5]
// len=5 cap=5 [0 0 0 0 0]
b := make([]int, 1, 5) // [0:1]
// len=1 cap=5 [0]
c := b[:2] // [0:2]
// len=2 cap=5 [0 0]
d := c[2:5] // [2:5]
// len=3 cap=3 [0 0 0]
Slice default
スライスを作るときの上限・下限の値は省略できる
// 長さが10のArrayがあったとすると、下記はどれも等価
a[0:10]
a[:10]
a[0:]
a[:]