About
他の言語やったことがある人, golangの復讐をしたい人向けのさっさと基本文法を学ぶためのまとめです
Hello World
まずは基本のHello World
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}
package
main以外のパッケージはディレクトリ構造と一致している必要がある.
package文
package pkg
import
packageのimport
import "pkg"
複数のパッケージをまとめてインポートすることもできる.
「標準パッケージ」「workspace上のパッケージ」「ローカル(相対パス」が使える.
import (
"pkg"
"pkg/sub"
a "abc" // 別名でのimport
. "def" // ドットでのimport(package名を省略できる
_ "ghi" // ブランクでのimport(init関数だけ実行され, packageは使用できない)
)
export
トップレベルで宣言された「関数/メソッド」「変数/定数」「型」が他のpackageから使用できる.
ただし, 大文字から始める必要がある.
type Hoge struct {
Name string // public扱い
age int // private扱い
}
func Hello() {
fmt.Println("public扱い外部から使用できる")
}
func hello() {
fmt.Println("private扱い外部から使用できない")
}
init関数
packageの初期化処理のために一度だけ実行される特殊な関数(プログラム実行時に最初に呼び出される).
複数定義できるが呼び出し順は決まっていない.
func init() {
fmt.Println("init1")
}
func init() {
fmt.Println("init2")
}
internal
vendor
基本文法
コメント
// 行コメント
/*
ブロックコメント
*/
行末
基本的に改行を行末として扱う. 「;(セミコロン)」でも行末.
var v1 = 1
var v2 =
2
識別子
基本的なプログラミング言語と同じ([a-zA-Z_][a-zA-Z_0-9]*
).
アンダーバー1個の特殊な(ブランク)識別子は使用しないことを明示することでエラーを防ぐ.
var _ = 100
v, _ := Func() // 2個目の戻り値は使わない
型
基本型
種類 | 型名 | サイズ | 符号 | デフォルト値 | 説明 |
---|---|---|---|---|---|
整数 | int | 4/8 | ○ | 0 | 符号付き整数. サイズはCPU依存 |
〃 | int8 | 1 | ○ | 0 | int(8ビット) |
〃 | int16 | 2 | ○ | 0 | int(16ビット) |
〃 | int32 | 4 | ○ | 0 | int32(ビット) |
〃 | int64 | 8 | ○ | 0 | int64(ビット) |
〃 | uint | 4/8 | ○ | 0 | 符号なし整数. サイズはCPU依存 |
〃 | uint8 | 1 | × | 0 | uint(8ビット) |
〃 | uint16 | 2 | × | 0 | uint(16ビット) |
〃 | uint32 | 4 | × | 0 | uint(32ビット) |
〃 | uint64 | 8 | × | 0 | uint(64ビット) |
〃 | byte | 1 | × | 0 | uint8の別名 |
〃 | rune | 4 | ○ | 0 | int32の別名. Unicodeコードポイントを格納 |
〃 | uintptr | - | × | 0 | ポインタの値を数値で格納. サイズはCPU依存 |
浮動小数点数 | float32 | 4 | - | 0.0 | 32ビット浮動小数点数(IEEE754準拠) |
〃 | float64 | 8 | - | 0.0 | 64ビット浮動小数点数(IEEE754準拠) |
複素数 | complex64 | 8 | - | 0.0 | float32の実数と虚数を持つ複素数 |
〃 | complex128 | 16 | - | 0.0 | float64の実数と虚数を持つ複素数 |
文字列 | string | - | - | "" | 文字列を格納 |
真偽値 | bool | - | - | false | 真(true)または偽(false)を格納 |
複合型
種類 | 型名 | 説明 |
---|---|---|
配列 | [n]T | 固定長配列 |
スライス | []T | 可変長配列 |
ポインタ | *T | アドレスを格納 |
マップ | map[Tk] Tv | マップ(key-value)のペアを格納 |
チャネル | chan T | 並列処理時の通信用 |
関数 | func(T1)T2 | 関数を格納 |
構造体 | struct{ ... } | 構造体(フィールドの集合) |
interface | interface{ ... } | インターフェース(メソッドの集合) |
変数宣言
golangの変数宣言
- 型名が変数名のあと(var 変数名 型名)
- varキーワードを使う(:=で省略可能)
- 型推論できる
var v1 int // 初期値なし(デフォルト値で初期化)
var v2 int = 10 // 初期値あり
var v3 = 10 // 型の省略(初期値の指定の必要あり)
var v4, v5 int // 複数の変数(初期値なし)
var v6, v7 int = 10, 20 // 複数の変数(初期値あり)
var v8, v9 = 10, 20 // 複数の変数(型省略)
var v10, v11 = func() // 戻り値が2個の関数
var ( // まとめて宣言
v12 int = 100
v13 = 200
)
v14 := 300 // var v14 = 300と同じ
リテラル
// 整数リテラル(int型)
dec := 1234
oct := 01234
hex := 0xabc
// 浮動小数点リテラル(float64型)
f1 := 1.2
f2 := 1.2e-2 // 指数表記
// 虚数リテラル(complex128型)
i1 := 1.2i
i2 := 1.2i+3
i3 := complex(1.2, 2.3) // 組み込み関数complexで複素数を作成
real(i3) // 組み込み関数realで実数部を取得
imag(i3) // 組み込み関数imagで虚数部を取得
// 文字
r := 'a' // rune(Unicodeコードポイント)文字
s := "abcdefg" // 文字列
raw := `abcd
efg` // raw(生)文字列
// 複合リテラル
v1 := [3]int {1,2,3} // 配列
v2 := []int {1,2,3} // スライス
v3 := map[string]int {"a":1} // マップ
v4 := struct{ a int} {a: 1} // 構造体
// 関数リテラル
f := func(a int) int {return a}
f(3)
定数
const (
c1 uint = 1 // uint型
c2 = c1 // uint型
c3 = 3 // 型なし
)
const (
u1 uint = 1 // 1
u2 = 1 // 1
u3 = 1 // 1
)
iota
enumのような連続した値の定数を作成できる
package main
import (
"fmt"
)
const (
a0 = iota // 0
a1 = iota // 1
a2 = 10 // 10
a3 = iota // 3(iotaの値は()内での行数で決まる)
a4, a5 = iota, iota // どちらも4
)
const (
b0 = iota // 0
b1 // 1(省略可能. 1行目のiota式を使用)
b2 // 2(〃)
_ // 3(ブランクによるスキップ
b3 // 4
b4 = iota * iota // 25(5*5)
)
const (
c0 = 1 << iota // 1(1 << 0)
c1 // 2(1 << 1)
c2 // 4(1 << 2)
c3 // 8(1 << 3)
)
func main() {
fmt.Println(a0, a1, a2, a3, a4, a5)
fmt.Println(b0, b1, b2, b3, b4)
fmt.Println(c0, c1, c2, c3)
}
キャスト
v1 := int64(100) // int->int64
s1 := []byte("abc") // string->[]byte
s2 := string(s1) // []byte->string
条件分岐(if文)
golangのif文
- ()で条件式を囲わない
- 条件式は真偽値型でなくてよい
- else if/elseは前の条件ブロック{}の後に書く. 改行しない
- if文(elseブロックの最後まで)だけのスコープを持つ変数を宣言できる(他の制御構文も同様)
if 条件式1 {
// 条件式1がtrueのとき実行
} else if 条件式2 {
// 条件式2がtrueのとき実行
} else {
// それ以外のとき実行
}
// if文での変数宣言
// 初期化文で使える文
// 変数宣言(:=), 代入文, ++, --, 関数, メソッド呼び出し, チャンネル送信・受信
if 初期化文; 条件式1 {
...
} else if 初期化文; 条件式2 {
...
} else {
...
} // スコープはここまで
条件分岐(switch文)
golangのswitch文
- break文を書かなくても次のcaseが実行されない(実行するにはfallthrough文を使う)
- case文に条件式を書くこともできる(trueの場合実行)
switch 式1 {
case 式2:
...
case 式3, 式4:
// 式3と式4のいずれかと一致した場合実行
...
case 式5:
...
// 次のcaseも実行される
fallthrough
case 式6:
...
default:
...
}
switch { // ここには式を書かない
case 条件式1:
...
case 条件式2:
...
default:
...
}
繰り返し(for文)
golangのfor文
- 繰り返しはfor文だけ(while文/do while文はない)
- 基本はC言語などと同じ
- 多重for文はラベル指定のbreak文/continue文で抜ける(goto文でも可能)
- 拡張for文(foreach)は組み込み関数rangeを使う
for 初期化文; 条件式; 更新処理文 {
...
}
// 条件式のみ(while)
for 条件式 {
...
}
// 無限ループ
for {
...
}
// ラベルで外部ループにジャンプ
Loop:
for ... {
for ... {
if ... {
continue Loop
}
if ... {
break Loop
}
}
}
package main
import (
"fmt"
)
func main() {
// 文字列
// 第1変数:バイト単位の位置
// 第2変数:rune(Unicodeコードポイント)
s := "hoge"
for i, c := range(s) {
fmt.Println(i, string(c)) // rune->string
}
// 配列・スライス
// 第1変数:インデックス
// 第2変数:要素
a := []int {1,3,5}
for i, v := range(a) {
fmt.Println(i, v)
}
// マップ
// 第1引数:キー
// 第2引数:バリュー
m := map[string]int {"a":1, "b":2, "c":3}
for k, v := range(m) {
fmt.Println(k, v)
}
// チャネル
// 第1引数:受信した値
// 第2引数:なし
ch := make(chan int)
go func() {ch<- 10}()
for v := range(ch) {
fmt.Println(v)
}
}
関数
golangの関数
- funcキーワードを使う
- 2個まで返すことできる
- 戻り値に名前を付けることができる
- 関数終了時に呼び出す関数を登録するdefer文
// 引数・戻り値なし
func Fn1() {
...
}
// 引数あり
func Fn2(s string, b int) {
...
}
// 同じ型ならまとめられる
func Fn3(a, b int) {
...
}
// 可変長引数
func Fn4(s ...string) {
...
}
// 戻り値1個
func Fn5() int {
return 1
}
// 戻り値2個
func Fn6() (int, int) {
return 1, 2
}
// 名前付き関数
func Fn7() (v int) {
...
return // 自動的に名前付きの変数が返される
...
return 10 // 値をしてした場合, 指定した値が優先される
}
// defer文
// 関数はスタックに格納される(LIFO. 最後のdefer文が先に実行される)
// 関数終了時に実行するべき関数を登録しておく
func Fn8() {
defer f(1)
defer f(2)
defer f(3)
}
型宣言
golangの型宣言
- typedefに相当する
- 基本型・複合型から新しい型を作成する
- 元の型<->新しい型(代入可能・型変換可能)
- 同じ基底型を持つ型同士(代入不可・型変換可能)
// type 新しい型 元の型
type myInt int
type myAry [10]int
構造体
golangの構造体
- 継承はできない
- 匿名フィールドに構造体を埋め込むことはできる
- 各フィールドにタグ情報を埋め込める(xml,jsonのパースなどに使える)
package main
import (
"fmt"
)
type Person struct {
Name string
Age int `key:"value"` // タグ情報
}
type Worker struct {
Person // 匿名フィールド
Work string
}
func main() {
var p1 Person
p1.Name = "aiueo"
p1.Age = 20
p2 := Person{Name: "abcde", Age: 40}
p3 := Worker{p2, "NEET"}
fmt.Printf("%#v\n", p1)
fmt.Printf("%#v\n", p2)
fmt.Printf("%#v\n", p3)
}
ポインタ
golangのポインタ
- 基本はC言語などと同様
- &(アドレス参照演算子), *(間接参照演算子)を使う
- 組み込み関数new(型)でメモリを確保する
- 参照先のないポインタは
null
ではなくnil
- GC(ガベージコレクション)で自動的にメモリは開放される
- 危険なポインタアクセスはできない(unsafeパッケージで扱える)
package main
import (
"fmt"
)
func main() {
v := 10
p := new(int)
*p = 100
p1 := &v
fmt.Println(*p)
fmt.Println(*p1)
}
メソッド
golangのメソッド
- typeで作成した型に関数(メソッド)を持たせることができる
- 値レシーバ・ポインタレシーバ
package main
import (
"fmt"
)
type Int int
type St struct {
x int
}
func (v Int) add(n int) Int {
return v + Int(n)
}
func (st *St) add(n int) {
st.x += n
}
func main() {
v1 := Int(10)
v1 = v1.add(5)
fmt.Println(v1)
st := St{x:20}
st.add(5)
fmt.Println(st)
}
インターフェース
golangのインターフェース
- メソッドの集合
- インターフェースを実装するにはすべてのメソッドを実装する
- 構造体と同様に匿名フィールドにインターフェースを埋め込むことはできる
- interface{}はすべての型の値が代入可能
- interface型の「==」演算子は同じ型かつ同じ値のときのみ
true
を返す -
nil
は型なしのnil
とのみ一致する. 型がある場合はそれぞれのポインタ型のnil
と一致する - 元の型に戻すには型アサーションを使う(第1戻り値に変換した値, 第2戻り値に真偽値)
- 型switch文で型ごとの処理ができる
package main
import (
"fmt"
)
type IAnimal interface {
Name() string
Cry() string
}
type Cat struct {
name string
}
type Dog struct {
name string
}
func (a Cat) Name() string {
return a.name
}
func (a Cat) Cry() string {
return "にゃん"
}
func (a Dog) Name() string {
return a.name
}
func (a Dog) Cry() string {
return "わん"
}
func main() {
var a IAnimal
a = Cat{"みけ"}
fmt.Println(a.Name(), a.Cry())
a = Dog{"ぽち"}
fmt.Println(a.Name(), a.Cry())
var n interface{} // なんでも代入できる
n = 10
n = "abc"
fmt.Println(n)
}
package main
import (
"fmt"
)
func main() {
var i0 interface{} = 10
var i1 interface{} = "a"
var i2 interface{} = 10
var v0 int = 20
var v1 string = "b"
fmt.Println(i0 == i1) // false(型だけの一致)
fmt.Println(i0 == v0) // false(値だけの一致)
fmt.Println(i1 == v1) // false(両方の不一致)
fmt.Println(i0 == i2) // true(両方の一致)
// 元の型に戻すことで代入可能
v0, ok := i0.(int)
if ok {
fmt.Println("int変換成功", v0)
}
v1, ok = i1.(string)
if ok {
fmt.Println("string変換成功", v1)
}
switch v2 := i1.(type) {
case int:
fmt.Println("int", v2)
case string:
fmt.Println("string", v2)
case float32, float64:
fmt.Println("float", v2)
}
}
配列・スライス・マップ
golangの配列・スライス
- []を型名の前に書く
- 固定長を配列, 可変長をスライス
- 配列は値型, スライスは参照型
- 組み込み関数appendで拡張可能(容量が不足なら再割当てされる)
- 組み込み関数makeでメモリ確保
- 組み込み関数len, capで長さ, 容量を取得
- 組み込み関数copyでスライスをコピー(コピー先の長さまでがコピー元の要素で上書きされる)
- スライス式でスライスの一部を取り出し(0<=start<=end<=len, 新しいスライスの容量はlen-start)
- フルスライス式で取り出す容量を制限(0<=start<=end<=max<=len, 新しいスライスの容量はmax-start)
golangのマップ
- マップの型はmap[キーの型]要素の型
- マップ[キー]で参照(第1戻り値が要素またはデフォルト値, 第2戻り値に真偽値)
- 組み込み関数makeで要素数を指定した方が高速
配列(固定長)
ary1 := [3]int{11, 12} // 長さ指定([2]はデフォルト値)
ary2 := [...]int{21, 22, 23} // 長さ指定なし
ary3 := [3]int{0: 31, 2:32} // インデックス指定
length := len(ary1) // 長さの取得
スライス(可変長)
slice1 []int // nil
slice2 []int{11,12,13} // 長さ2
slice3 []int{21,22,23} // 長さ3
append(slice2, 14, 15, ...) // 要素を追加(可変長引数)
append(slice1, slice3...) // スライスを追加
slice4 := make([]int, 5) // 長さ5(容量5)のスライスを確保(デフォルト値を設定)
slice5 := make([]int, 5, 10) // 長さ5・容量10のスライスを確保(デフォルト値を設定)
capacity := cap(slice5) // 容量の取得
copy(slice5, slice2) // slice5(長さ5)にslice2の要素が5つまで上書きコピー
slice6 := slice5[1:3] // [1]-[3-1]
slice7 := slice5[2:] // [2]-[len-1](最後)
slice8 := slice5[:2: // [0](最初)-[2-1]
slice9 := slice5[:] // [0](最初)-[len-1](最後)(コピー)
slice10 := slice5[1:3:5] // [1]-[3-1]を容量(5-1)
マップ
map1 := map[string]int{"a":1, "b":2, "c":3}
k, v := map1["key"]
if v {
fmt.Println(k)
} else {
fmt.println("キーが存在しない")
}
map2 := make(map[string]int) // 要素数の指定なし
map3 := make(map[string]int, 10) // 要素数の指定あり
エラー
- errorインターフェス
- panic
並列処理(ゴルーチン)
- 標準で並列処理ができる
-
go
を関数に付けて呼び出すと別ルーチンで実行 - 通信用のチャネル型変数を使う
- バッファなしチャネルは同期通信, バッファ付きチャネルは非同期通信(容量まで貯める)
- 組み込み関数closeでチャネルを閉じると受信以外できなくなる(クローズされている場合, 第1戻り値にデフォルト値, 第2戻り値に真偽値)
- for rangeはチャネルがクローズされるまで繰り返す
- select文を使うことで複数のチャネルを扱うとき送信待ち・受信待ちで他の操作ができなくなるのを防ぐ
chan int // 送受信可能なチャネル型
chan<- int // 送信のみ可能なチャネル型
<-chan int // 受信のみ可能なチャネル型
make(chan int) // バッファなしチャネル
make(chan int, 5) // バッファ付きチャネル
ch <- 10 // チャネル送信文
<-ch // チャネル受信
close(ch)
n, t := <-ch
if t {
fmt.Println(n)
}
// クローズするまで繰り返す
for n := range ch {
fmt.Println(n, "を受信")
}
func Fn(ch1 chan<- int, ch2 <-chan string) {
var s string
select {
case ch1 <- 10:
fmt.Println("ch1送信")
case s = <- ch2:
fmt.Println("ch2受信")
// 受信も送信もできない場合
default:
fmt.Println("まだだよ")
}
}