概要
Go言語をおさらいする必要があったので、公式 で勉強しながらチートシートを作りました。
間違いや情報が古い等ありましたらコメント頂けると幸いです。
内容
A Tour of Go の順番で作った。
- パッケージ
- 変数
- 関数
- 基本制御構文
- 遅延処理
- ポインタ
- 構造体
- 配列
- スライス
- マップ
- メソッド
- インタフェース
- エラー
- その他標準ライブラリ
- 非同期処理
パッケージ
Go のプログラムは package で構成される。
プログラムは main パッケージから開始される。
fmt
等は標準のパッケージ、import で読み込む。
変数
// 通常の宣言
var x int
// 型名省略可能
var x, y = 1, "true"
// 暗黙的な型宣言
x := 1
// 定数
const X = 1
関数
一般的な言語の関数と変わらない。
ただし、Goの関数はクロージャの為、それ自信の外部から変数を参照する。
// 構文
func 関数名(変数名 型名, 変数名 型名) 返り値型名 { }
// 大文字で始まる関数はパッケージ外からアクセス可能
func Add(x int, y int) int { }
// 小文字で始まる関数はパッケージ外からアクセス不可
func add(x int, y int) int { }
// 引数の型が同じ場合はまとめる
func add(x, y int) int { }
// 関数値
f := func(x, y int) int { }
f(1, 2)
基本制御構文
// 繰り返し
for i := 0 ; i < 10 ; i++ { }
// while
for i < 10 { }
// 無限ループ
for { }
// 分岐
if x < 0 { }
// ifのショートステートメント
if v := add(x, y); v < 10 { }
// switch
switch v := add(x, y); v {
case 1:
// 処理 (breakは必要無い)
default:
// 処理
}
// 条件の無いswitch
switch {
case v < 10:
// 処理
}
遅延処理
defer
を使用。
defer が付いている関数呼び出しは関数の return 時に行われる。
ただし、引数の評価はすぐに行われる。
defer add(x, y)
ポインタ
Goはポインタを扱い、ポインタは値のアドレスを指す。
ゼロ値のポインタは nil になる。
// ポインタ
var p *int
// &演算子 (ポインタを引き出す)
i := 42
p = &i
// *演算子 (ポインタの指す変数を示す)
fmt.Println(*p) // 42
*p = 21 // ポインタpを通して変数iに代入
構造体
構造体はフィールドの集合である。
先頭大文字、小文字のルールは関数と同じ。
// 構造体の定義
type Point struct {
X int
Y int
}
// 構造体の初期化
p := Point{1, 2}
// フィールドを指定して初期化
p := Point{X: 1, Y: 2}
// 省略して初期化 (ゼロ値が入る)
p := Point{X: 1}
p := Point{}
// ポインタを返して初期化
v := &Point{1, 2}
// フィールドへアクセス
p.X = 10
// ポインタを通してアクセス
v := &p
(*v).X = 15
v.X = 5 // *演算子は省略可能
配列
配列の長さは型の一部分であるため、サイズを変えることは出来ない。
// 宣言
var a [3]int
// 初期化
a := [3]int{1,2,3}
// アクセス
a[0] = 1
スライス
配列は固定長だが、スライスは可変長。
Goでは配列よりもスライスの方が一般的。
スライスは配列への参照のようなもので、実際にデータを持っていない。
スライスは配列の部分列を指し示し、スライスの変更は配列にも影響する。
// 宣言
var s []int
// 既存の配列からスライスを作る
var s []int = a[1:4] // 1番目から3番目の要素で作る
// スライスの省略
var s[]int = a[:4] // 0番目から3番目
var s[]int = a[1:] // 1番目から最後尾まで
var s[]int = a[:] // 0番目から最後尾まで
// 新規の配列からスライスのみを取得する
s := []int{1, 2, 3}
// 長さ (スライスの要素数) の取得
len(s)
// 容量 (スライスの最初の要素から数えて、元配列の要素数) の取得
cap(s)
// 動的サイズの配列の作成
s := make([]int, 長さ, 容量)
// 多次元スライス
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
board[0][0] = "X"
// 要素の追加 (長さは追加した数だけ増え、容量は足りない時に多めに自動確保する)
append(s, 追加したい値, 追加したい値...)
// リストの走査 (i はインデックス, v は値のコピー)
for i, v := range s { }
for _, v := range s { } // インデックスの破棄
for i := range s { } // 値の破棄
マップ
map はキーと値を関連付ける。
// 宣言
m := make(map[string]int)
// リテラル
var m = map[string]int{
"one": 1,
"two": 2,
}
// アクセス
m["one"] = 1
// 要素の削除
delete(m, key)
// キーの存在確認
elem, ok := m[key] // 存在すれば ok は true, しなければ false
メソッド
Go にはクラスが無いが、型にメソッドを定義出来る。
メソッドはレシーバ引数を関数に取る。
// Point型を定義
type Point struct {
X, Y int
}
// Point型にメソッドを定義
func (p Point) Add() int {
return p.X + p.Y
}
// メソッド呼び出し
p := Point{1, 2}
p.Add()
// ポインタレシーバ (レシーバ自身を更新出来る)
func (p *Point) Up() {
p.X += 1
}
インタフェース
interface型はメソッドのシグネチャの集まりで定義される。
これを実装した値を、interface型の変数へ持たせることが出来る。
JavaとかのInterfaceと雰囲気同じ。
// 型定義
type Point struct {
X, Y int
}
// インタフェース定義
type Calculator interface {
Add(int, int) int
}
// インタフェース実装 (implementsキーワードは必要無い)
func (p Point) Add(x, y int) int {
return p.X + p.Y
}
// 代入
var c Calculator
p := Point{1, 2}
c = &p
エラー
Go言語では、エラー状態を error 値で表現する。
error 型は組み込みインタフェースである。
// error型の定義
type error interface {
Error() string
}
// エラーチェック (nilなら成功, nilでなければ失敗)
i, err := f()
if err != nil {
fmt.Printf("%v¥n", err)
return
}
その他標準ライブラリ
- データストリームの読み込み
- インタフェース
- ファイル
- ネットワーク接続
- 圧縮
- 暗号化
- etc...
詳細は公式を参照。
非同期処理
goroutine を利用する、これはGoのラインタイムに管理される軽量スレッドである。
go f(x)
と書くと新しい goroutine が実行される。
引数の評価は呼び出し元の goroutine で行われ、関数の実行は新しい goroutine が作られる。
goroutine は同じアドレス空間を利用するので、共有メモリアクセスは必ず同期する。
同期には channel を利用すると良い。
詳しくは公式参照。