LoginSignup
7
7

More than 3 years have passed since last update.

Goで地味だけど知っておいて損はないこと

Last updated at Posted at 2018-06-13

マップ編

  • マップにおいて、最後の要素の後にも","をつける(つけないと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 {
           ()
    }
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7