Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
10
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

GO言語のcompressパッケージで陥りがちな罠

GO言語の便利な機能に defer があります。
共通の後処理を1つにすることが出来るので、ちょっとこの言語を齧り出すと、何も考えずに片っ端から、

a.Open()
defer a.Close()
b.Open()
defer b.Close()

という書き方をするようになります。

大抵の場合はこれで問題ないのですが、compress下のパッケージに罠が仕掛けられており、ワタシのように、何も考えずに使うと見事に引っかかります。
下記が、迂闊に何も考えずに書いたコードです。


import (
   // ...
  "compress/gzip"   // compress/zlib等でも同様
   // ...
)

func compress(data string) ([]byte, error) {
    b := new(bytes.Buffer) 
    gz := gzip.NewWriter(b)
    defer gz.Close() // これ

    if _, err := gz.Write([]byte(data)); err != nil {
        return nil, err
    }
    if err := gz.Flush(); err != nil {
        return nil, err
    }
    return b.Bytes(), nil
}

上記のコードの場合、

  1. gz.Write()
  2. gzFlush()
  3. b.Bytes()
  4. gz.Close()

の順に処理されます。
Flushを呼んでいるので、一見問題ないように思うのですが、このcompress下のパッケージ、Close時に最終的なデータを確定します。なので、値が確定する前に値を返してしまい、それが圧縮後のデータと思い込んで保存すると、解凍する際に、 “unexpected EOF” エラーという地獄が発生します。
地獄に落ちずに畜生界に留まり続けるためには、下記のように、しっかりCloseする必要があります。


func compress(data string) ([]byte, error) {
    b := new(bytes.Buffer) 
    gz := gzip.NewWriter(b)

    if _, err := gz.Write([]byte(data)); err != nil {
        gz.Close()
        return nil, err
    }
    if err := gz.Flush(); err != nil {
        gz.Close()
        return nil, err
    }
    gz.Close() // これ
    return b.Bytes(), nil
}

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
10
Help us understand the problem. What are the problem?