Slide 1

Slide 1 text

Why should we use bufio.ReadBytes, ReadString instead of ReadSlice in most cases? fukuoka.go, JULY 14 2020 #ちょっと深堀り #コードリーディング #社内でGophersCodeReadingPartyはじめました #fukuokago Presented by @hgsgtk

Slide 2

Slide 2 text

{ About me GitHub/Twitter: @hgsgtk BASE BANK, Inc. (BASE, Inc.) 東京からお邪魔してます #fukuokago

Slide 3

Slide 3 text

#社内でGophersCodeReadingPartyはじめました ● 有志でゆるくやっている の勉強会 ● 気になる のあの挙動「どう実装されてるの?」を実装で追っ ていって、最終的に勉強になってしまう会 ● 事前準備なし、気になるツール・ライブラリの中身を、読んで みる ● 週

Slide 4

Slide 4 text

#社内でGophersCodeReadingPartyはじめました #社内でGophersCodeReadingPartyはじめました mackerelio/mackerel-agent-plugin GoogleCloudPlatform/terraformer Songmu/kibelasync terraform-providers/terraform-provider-aws kayac/ecspresso hashicorp/terraform mackerelio/mkr

Slide 5

Slide 5 text

パーティ中の一コマ... この bufio パッケージの説明どういうこ とだろう...?

Slide 6

Slide 6 text

“Because the data returned from ReadSlice will be overwritten by the next I/O operation, most clients should use ReadBytes or ReadString instead.” godoc.org bufio package func (*Reader) ReadSlice https://godoc.org/bufio#Reader.ReadSlice

Slide 7

Slide 7 text

“ReadLine is a low-level line-reading primitive. Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner.” godoc.org bufio package func (*Reader) ReadLine https://golang.org/pkg/bufio/#Reader.ReadLine

Slide 8

Slide 8 text

#ちょっと深堀り ● の仕様・ ユーザーとしての使い方 ● の関係性 ● が内部実装で何をやってくれているか

Slide 9

Slide 9 text

ReadSlice の仕様 戻り値が上書きされる例 #1 https://play.golang.org/p/Drl1FjauMeM (Copyright: 同僚の @budougumi0617 さんの再現コード) ← 1. buffer から読み取る ← 2. 内部の buffer が上書きされるような操作をする ← 3. 再度 “1.” で取得した値を表示 4. ReadSlice の場合は、元のbufferのsliceの ため、 bufferの上書きにより、中身が変わっている

Slide 10

Slide 10 text

ReadSlice の仕様 戻り値が上書きされる例 #2 https://play.golang.org/p/BRhnRMsFa59 ← 1. buffer から読み取る ← 2. 内部の buffer が上書きされるような操作をする ← 3. 再度 “1.” で取得した値を表示 4. ReadSlice の場合は、元のbufferのsliceの ため、 bufferの上書きにより、中身が変わっている

Slide 11

Slide 11 text

“ReadSlice reads until the first occurrence of delim in the input, returning a slice pointing at the bytes in the buffer.” godoc.org bufio package func (*Reader) ReadSlice https://godoc.org/bufio#Reader.ReadSlice → 戻り値は、bufferのsliceなので後続のbufferへの操作で中身が変わる

Slide 12

Slide 12 text

ReadBytes, ReadString, ReadSlice, ReadLine の関係性 Reader.ReadString(delim byte) (string, error) Reader.ReadBytes(delim byte) ([]byte, error) Reader.ReadSlice(delim byte) ([]byte, error) call call → を使用すべきなのはなぜ? Reader.ReadLine() ([]byte, bool, error) call

Slide 13

Slide 13 text

ReadString -> ReadBytes https://golang.org/src/bufio/bufio.go?s=11350:11404#L474 func (b *Reader) ReadString(delim byte) (string, error) { bytes, err := b.ReadBytes(delim) return string(bytes), err } ReadString は ReadBytes の戻り値 []byte を string 型に変換するだけ

Slide 14

Slide 14 text

ReadBytesが内部実装で何をやってくれているか https://golang.org/src/bufio/bufio.go?s=11350:11404#L419 ← ReadSlice を call ← !! 新たなバッファを用意し値をコピー そのため、もともとのバッファに対するI/O操作の 影響を受けなくなる

Slide 15

Slide 15 text

slice の 配列ポインタを比較してみる https://play.golang.org/p/Vom_IJf8Ua7 ← 1. もともとの[]byte の .pointer ← 2. ReadSlice / ReadBytes で取得し た []byte の .pointer ↓ 3. ReadSlice は同じアドレス ReadBytes の場合は違うアドレスと なってる

Slide 16

Slide 16 text

“in most cases” に当てはまらないときって? ● メモリ使用量が気になるケースで、 の 使用に利点がありそう ● For many applications, a single line is sufficient: hence, some commenters suggested ReadLine() or something similar.

Slide 17

Slide 17 text

まとめ #ちょっと深堀り ● の戻り値は、 の なので後 続の への操作で中身が変わる ● では、コピーしているため、元のバッファに対する オペの影響を受けない ● メモリ効率が気になる場合だと、 を用 いるという判断もありそう

Slide 18

Slide 18 text

まとめ #社内でGophersCodeReadingPartyはじめました ● 業務ではスルーしがちな、細かい標準パッケージ、中を見る いい機会になる ● 身近な興味のあるツールやライブラリを起点にすると、ユース ケースをイメージしやすい ● 時代に、他チームとのコミュニケーションの機会になって 良いという声も