如何避免对同一内存块进行切片引用

huangapple go评论89阅读模式
英文:

How to avoid slice reference to the same memory block

问题

我在从数据库查询并尝试插入到一个包含一些map[string]interface{}的切片时遇到了一个问题。即使我已经使用make创建了一个新的内存块,切片似乎总是映射到同一个内存块。

以下是翻译好的代码:

type DBResult []map[string]interface{}

func ResultRows(rows *sql.Rows, limit int) (DBResult, error) {
    cols, err := rows.Columns()
    if err != nil {
        return nil, err
    }

    vals := make([]sql.RawBytes, len(cols))
    scanArgs := make([]interface{}, len(vals))
    for i := range vals {
        scanArgs[i] = &vals[i]
    }

    if limit > QUERY_HARD_LIMIT {
        limit = QUERY_HARD_LIMIT
    }

    res := make(DBResult, 0, limit)
    for rows.Next() {
        err = rows.Scan(scanArgs...)
        m := make(map[string]interface{})

        for i := range vals {
            m[cols[i]] = vals[i]
        }
        /* Append m to res */
        res = append(res, m)
        /* The value of m has been changed */
        fmt.Printf("lib: m:\n\n%s\n\n", m)
        /* When printing res, always mapping to the same memory block */
        fmt.Printf("lib: res:\n\n%s\n\n", res)
    }

    return res, err
}

以下是结果,你可以发现res的内容是相同的:

m = map[comment:first_comment id:0]

res = [map[id:0 comment:first_comment]]

m = map[id:1 comment:first_comment]

res = [map[id:1 comment:first_comment] map[id:1 comment:first_comment]]

m = map[id:2 comment:first_comment]

res = [map[id:2 comment:first_comment] map[id:2 comment:first_comment] map[id:2 comment:first_comment]]

我期望的res是 [map[id:0 comment:first_comment] map[id:1 comment:first_comment] map[id:2 comment:first_comment]]

谢谢观看。

英文:

I met a problem when query from Database and tried to insert into a slice(contains some map[string]interface{})
Even I already used make to create a new memory block, the slice seems always mapping to a same memory block.

type DBResult []map[string]interface{}

func ResultRows(rows *sql.Rows, limit int) (DBResult, error) {
    cols, err := rows.Columns()
    if err != nil {
        return nil, err
    }

    vals := make([]sql.RawBytes, len(cols))
    scanArgs := make([]interface{}, len(vals))
    for i := range vals {
        scanArgs[i] = &vals[i]
    }

    if limit > QUERY_HARD_LIMIT {
        limit = QUERY_HARD_LIMIT
    }

    res := make(DBResult, 0, limit)
    for rows.Next() {
        err = rows.Scan(scanArgs...)
        m := make(map[string]interface{})

        for i := range vals {
            m[cols[i]] = vals[i]
        }
        /* Append m to res */
        res = append(res, m)
        /* The value of m has been changed */
        fmt.Printf("lib: m:\n\n%s\n\n", m)
        /* When printing res, always mapping to the same memory block */
        fmt.Printf("lib: res:\n\n%s\n\n", res)
    }

    return res, err
}

The following is the result, you can find the contents of res are the same

>m = map[comment:first_comment id:0]
>
>res = [map[id:0 comment:first_comment]]
>
>m = map[id:1 comment:first_comment]
>
>res = [map[id:1 comment:first_comment] map[id:1 comment:first_comment]]
>
>m = map[id:2 comment:first_comment]
>
>res = [map[id:2 comment:first_comment] map[id:2 comment:first_comment] map[id:2 comment:first_comment]]

My expect of res = [map[id:0 comment:first_comment] map[id:1 comment:first_comment] map[id:2 comment:first_comment]]

Thanks for watching

答案1

得分: -1

根据Rows文档(https://golang.org/pkg/database/sql/#Rows.Scan):


Scan函数将当前行的列复制到dest指向的值中。

如果参数的类型是*[]byte,Scan会将相应数据的副本保存在该参数中。该副本由调用者拥有,可以进行修改并持有时间不确定。可以通过使用类型为*RawBytes的参数来避免复制;有关其使用限制的详细信息,请参阅RawBytes的文档。

如果参数的类型是*interface{},Scan会直接复制底层驱动程序提供的值,而不进行转换。如果该值的类型是[]byte,则会进行复制,并且调用者拥有结果。


在你的情况下,你使用了RawBytes作为Scan函数的参数。这可能是你遇到的问题所在。尝试使用其他类型的参数作为Scan函数的参数。

英文:

according to the document of Rows (https://golang.org/pkg/database/sql/#Rows.Scan):


Scan copies the columns in the current row into the values pointed at by dest.

If an argument has type *[]byte, Scan saves in that argument a copy of the corresponding data. The copy is owned by the caller and can be modified and held indefinitely. The copy can be avoided by using an argument of type *RawBytes instead; see the documentation for RawBytes for restrictions on its use.

If an argument has type *interface{}, Scan copies the value provided by the underlying driver without conversion. If the value is of type []byte, a copy is made and the caller owns the result.


in your case, you use RawBytes as argument of Scan. That might be the problem you got. Try other type as argument of Scan function.

huangapple
  • 本文由 发表于 2015年10月30日 20:17:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/33435807.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定