emahiro/b.log

Drastically Repeat Yourself !!!!

ポインタ型のvarでの初期化でハマった話

ハマったところ

CLIで特定の画像をbinary化してから画像にしてPostで送信する、というコードを書こうと思った時にポインタ型の初期化についてハマったのでメモ。

ざっくり書きたかったコードは以下(※ コードは適当)

func UploadImgFile() error {
  // 画像を開く
  f, _ := os.Open("imagePath")
  
  // form-dataを入れるバイナリを箱を用意
  buf := new(bytes.Buffer)
  
  // 箱は multipart型にする
  w := multipart.NewWriter(buf)
  
  // form-dataを作成する
  ff, _ := w.CreateFormFile("filedName", "test.png")
  
  // io.Copyでform-dataにOpenで取得した画像を書き込む
  if _ , err := io.Copy(ff, f); err != nil {
    panic(err)
  }
  w.Close()
  
  // upload処理hogehoge
  client := http.Client{}
  req, _ := http.NewRequest("POST", "Upload先のURL", buf)
  resp, _ := client.Do(req)
}

cf. multipart - The Go Programming Language

こういった処理を書こうと思った時の以下の箇所

// form-dataを入れるバイナリを箱を用意
buf := new(bytes.Buffer)

// 箱は multipart型にする
w := multipart.NewWriter(buf)

ここでのbytes.Bufferの初期化方法についてハマりました。

multipart型でWriterを初期化可能なパターン > The Go Playground

Writer初期化時点でpanicが起きるパターン > The Go Playground

panicになってしまうかどうかはぬるぽを踏んでしまうかどうか、という1点に尽きるのですが、panicになるケースにおいては ポインタ型の変数領域だけ宣言しても、初期値のままなんでゼロ値でポインタ型なのでnilになる

panicにならないケースについては newで領域確保してそのポインタを返しているのでnilでないのでうまくいく

multipart - The Go Programming Language

を見ると引数に指定するのは io.Writer 。しかし、io.Writer が実装されているのはポインタ型に対してなので、引数に渡すべき butes.Buffer はポインタ型でなければなりません。
cf. src/bytes/buffer.go - The Go Programming Language

そのため、varで単に初期化した場合でもアドレス型にして渡してあげればコンパイルエラーは起きず、処理を続けることが可能です。> The Go Playground

まとめ

golangのvarによる初期化についてはたまに思いっきりはまってしまうことが多いのでdocument読み返しながら進めないと思わぬ所で時間を食ってしまうなー。