dougua2309 2015-11-18 17:20
浏览 93
已采纳

指针问题

TL;DR Somehow, I am appending a pointer to a list instead of the object within a for loop of objects so at the end the entire slice is composed of the same object multiple times. I just don't know how to fix that.

The Long Way

I am still having a super hard time trying to figure out pointers in go. I posted a question yesterday and got some help but now I am stuck on a slightly different issue in the same piece of code.

I am working with gocql and cqlr go packages to try and bit a small object mapper for my Cassandra queries. Essentially the problem I am having is I am appending what appears to be a pointer to an object, not a new instance of the obj to the array. How do I fix that? I have tried adding & and * in front of value but that doesn't seem to work. How do I fix these? The bind function needs an & according to their docs.

Code

type Query struct {
    query       string
    values      interface{}
    attempts    int
    maxAttempts int
    structType  reflect.Type
}

func (query Query) RetryingQuery() (results []interface{}) {
    var q *gocql.Query
    if query.values != nil {
        q = c.Session.Query(query.query, query.values)
    } else {
        q = c.Session.Query(query.query)
    }

    bindQuery := cqlr.BindQuery(q)
    value := reflect.New(query.structType).Interface()
    for bindQuery.Scan(value) {
        fmt.Println(value)
        results = append(results, value)
    }
    return
}

The docs ask for var value type then in bind you would pass &value. I quoted the docs below.

var t Tweet
var s []Tweet
for b.Scan(&t) {
    // Application specific code goes here
    append(s, t)
}

The issue is I cannot directly go var value query.structType to define its type then pass the reference of that to bindQuery.Scan().

What is printed

&{result1 x86_64 24 3.2.0-74-generic Linux}
&{result2 x86_64 24 3.19.0-25-generic Linux}
&{result3 x86_64 4 3.13.0-48-generic Linux}
&{result4 x86_64 2 3.13.0-62-generic Linux}
&{result5 x86_64 4 3.13.0-48-generic Linux}

What is in the slice

Spoiler, it is result5 repeated over and over. I understand that I am just appending the pointer to same object to the list and that every loop iteration the object is changed and that changes all the results in the slice to that new object. I just don't know how to fix it.

[{"hostname":"result5","machine":"x86_64","num_cpus":4,"release":"3.13.0-48-generic","sysname":"Linux"},{"hostname":"result5","machine":"x86_64","num_cpus":4,"release":"3.13.0-48-generic","sysname":"Linux"},{"hostname":"result5","machine":"x86_64","num_cpus":4,"release":"3.13.0-48-generic","sysname":"Linux"},{"hostname":"result5","machine":"x86_64","num_cpus":4,"release":"3.13.0-48-generic","sysname":"Linux"},{"hostname":"result5","machine":"x86_64","num_cpus":4,"release":"3.13.0-48-generic","sysname":"Linux"}]
  • 写回答

1条回答 默认 最新

  • douliao7930 2015-11-19 10:07
    关注

    Well I can at least tell you what you're doing. bindQuery takes a pointer. It changes the value stored at the address.

    What you're essentially doing is this:

    package main
    
    import "fmt"
    
    func main() {
        var q int
        myInts := make([]*int, 0, 5)
        for i := 0; i < 5; i++ {
            q = i
            fmt.Printf("%d ", q)
            myInts = append(myInts, &q)
        }
        fmt.Printf("
    ")
        for _, value := range myInts {
            fmt.Printf("%d ", *value)
        }
        fmt.Printf("
    ")
        fmt.Println(myInts)
    }
    

    Which, as you can probably guess, gives you this:

    0 1 2 3 4 
    4 4 4 4 4 
    [0x104382e0 0x104382e0 0x104382e0 0x104382e0 0x104382e0]
    

    Things get a little more confusing with reflect. You can get your type as an interface, but that is it (unless you want to play with unsafe). An interface, in simple terms, contains a pointer to the original type underneath (and some other stuff). So in your function you are passing a pointer (and some other stuff). Then you're appending the pointer. It might be nice just to get concrete and type switch your interface. I assume you know what types it could be. In which case you'd have to have something along these lines:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type foo struct {
        fooval string
    }
    
    type bar struct {
        barval string
    }
    
    func main() {
        f1 := foo{"hi"}
        f2 := &foo{"hi"}
        b1 := bar{"bye"}
        b2 := &bar{"bye"}
    
        doSomething(f1)
        doSomething(f2)
        doSomething(b1)
        doSomething(b2)
    
    }
    
    func doSomething(i interface{}) {
        n := reflect.TypeOf(i)
        // get a new one
        newn := reflect.New(n).Interface()
        // find out what we got and handle each case
        switch t := newn.(type) {
        case **foo:
            *t = &foo{"hi!"}
            fmt.Printf("It was a **foo, here is the address %p and here is the value %v
    ", *t, **t)
        case **bar:
            *t = &bar{"bye :("}
            fmt.Printf("It was a **bar, here is the address %p and here is the value %v
    ", *t, **t)
        case *foo:
            t = &foo{"hey!"}
            fmt.Printf("It was a *foo, here is the address %p and here is the value %v
    ", t, *t)
        case *bar:
            t = &bar{"ahh!"}
            fmt.Printf("It was a *bar, here is the address %p and here is the value %v
    ", t, *t)
        default:
            panic("AHHHH")
        }
    }
    

    You could also just keep calling value = reflect.New(query.structType).Interface() inside of the loop which will give you new interfaces every time. Reassigning value after every append. Last time through the loop would make one extra though..

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型