マップ編
- マップにおいて、最後の要素の後にも","をつける(つけないとsyntax error、下の例もエラー)。
students := map[string]int{
"jon": 12,
"bob": 34
}
- キーの存在確認をしたいときは返ってくる2つ目の値を使う。
students := map[string]int{
"jon": 12,
"bob": 34,
}
age1, ok1 := students["jon"]
fmt.Println(age1, ok1) // 12 true
age2, ok2 := students["alice"]
fmt.Println(age2, ok2) // 0 false
JSON編
- json.Marshalでマーシャリングされるのは公開されているフィールドだけ(以下の例だとageが出力しない)。
- json.MarshalIndentを使うと人間が見やすいようにインデントしてくれる。
type User struct {
Name string
age int
Address string
}
func main() {
var users = []User{
{Name: "bob", age: 12, Address: "Tokyo"},
{Name: "jon", age: 23, Address: "Kyoto"},
}
data, err := json.Marshal(users)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
dataIndent, err := json.MarshalIndent(users, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
fmt.Printf("%s\n", dataIndent)
}
- 型の定義の後に `json:"~"` と書くと、json.Marshalした際に~で指定した名称になる。
さらにomitemptyをオプションでつけると、フィールドがその型のゼロ値(以下の例ではfalse)または空の場合JSONの出力をしない。
type User struct {
Name string `json:"username"`
Age int
Address string
Admin bool `json:"admin_user,omitempty"`
}
=>
[{"username":"bob","Age":12,"Address":"Tokyo","admin_user":true},{"username":"jon","Age":23,"Address":"Kyoto"}]
関数編
-
Goには、他の言語にあるようなキーワード引数やデフォルト引数はない。
-
引数は値渡しなので、関数はそれぞれの引数のコピーを受け取るため、コピーに対する修正は呼び出し元には影響しない。
※ポインタ、スライス、マップ等を使えば間接的に呼び出し元が修正を受ける場合もある。 -
可変個引数の関数を定義する場合は、最後のパラメータの型の前に省略記号の"..."をつける。
func main() {
fmt.Println(sum(1, 2, 3, 4, 5))
}
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
メソッド編
- setXYメソッドは*Pointレシーバを要求する場合、下記の2つの出力は同じになる。
これは2つめの例ではコンパイラが変数に対して暗黙的に&pを行なっているためです。
type Point struct{ X, Y int }
func main() {
p := Point{1, 1}
(&p).setXY(2, 3)
fmt.Println(p) // {2 3}
}
func (p *Point) setXY(x, y int) {
p.X = x
p.Y = y
}
type Point struct{ X, Y int }
func main() {
p := Point{1, 1}
p.setXY(2, 3) // 暗黙的に&pが行われる
fmt.Println(p) // {2 3}
}
func (p *Point) setXY(x, y int) {
p.X = x
p.Y = y
}
チャネル編
バッファなしチャネル
- チャネルを介してメッセージを送信する際、追加の情報を保持していない、
つまり目的が同期のみである場合には要素型が struct{} であるチャネルを使ってその点を強調する。
// チャネルの定義
ch := make(chan struct{})
// 送信側
ch <- struct{}{}
// 受信側
<-ch // 受信結果は破棄している
- チャネルを引数として使用する関数やメソッドにおいて、そのチャネルが受信専用、送信専用の場合は、
受信専用は<変数> <-chan <型>
, 送信専用は<変数> chan<- <型>
とすることで、
受信用のチャネルが送信に、送信用のチャネルが受信に使わる場合にコンパイルエラーとして検出できる。
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go first(ch1)
go second(ch1, ch2)
third(ch2)
}
func first(out chan<- int) {
x := 100
out <- x
}
func second(in <-chan int, out chan<- int) {
x := <-in
out <- x + 1
}
func third(in <-chan int) {
fmt.Println(<-in) // 101
}
コンパイルエラーにならないバージョン: https://play.golang.org/p/niSG6MZHcpy
コンパイルエラーになるバージョン: https://play.golang.org/p/WUgtBO6i2Ei
バッファありチャネル
- バッファありチャネルのバッファ容量を知りたい場合は
cap
、現在バッファされている要素の個数はlen
を使用する。
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
fmt.Printf("cap(ch) = %d\n", cap(ch)) // cap(ch) = 3
fmt.Printf("len(ch) = %d\n", len(ch)) // len(ch) = 2
}
その他
- Goを使用していると、以下のようなerrへの省略変数宣言(:=)をよく使用するが、
必ずしも左辺の全ての変数を宣言するわけでなく、以下の例ではerrが2回目に使われる部分では
変数宣言ではなく変数への"代入"になる。
ただし、左辺が両方とも代入になる場合はコンパイルエラーになるので、必ず1つは新たに変数宣言される必要がある。
resp, err := http.Get(url)
if err != nil {
return nil, err
}
・・・
doc, err := html.Parse(os.Stdin)
if err != nil {
(略)
}