9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-20

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
}

9
10
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
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?