標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

a[:0] と append の秘密

a = a[:0] は領域のサイズをリセットするが、a = make([]T,0,cap(a)) と違って、使っていたメモリブロックを再利用するため、allocation 回数を削減できる。

だが、旧a の領域が他で使われていないかを気にせず、無頓着に使うと append でおかしいことになる。たとえば

a := []string{ "a","b","c" }
b := a[:0]
b = append(b,"1")

fmt.Printf("%+v\n",a)

とすると a の内容が [1 b c] となる。これは append が領域を上書きで使用するためだ。

append 側で他所で使われているかチェックしてくれたらよさそうな話ではあるが、おそらく物理的に無理だろう。というのも参照カウンタ方式ではなく、マーク&スイープ方式の Garbage Collector を使っている場合、参照されている場所が自分を含めて2個以上あるか、調べようがないのだ。

となると、常に他で使われているという前提で append を実装しないと、上のような状況は避けられないわけだ。そういう append ちょっと作ってみよう。

package main

import (
    "fmt"
)

func appendStr(a []string, b string) []string {
    r := make([]string, len(a)+1)
    copy(r,a)
    r[len(a)] = b
    return r
}

func main() {
    a := []string{}
    for i := 0; i < 10; i++ {
        a = appendStr(a, fmt.Sprintf("%d", i))
    }
    fmt.Printf("%+v\n", a)
}

こんな実行効率わるそうなの、みんな使いたいと思います?結局、みんなオリジナル append を使いますよね? (でも、スクリプト言語では効率が悪くても平気でやってそうだなぁ)。