指针问题

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

Pointer problems

问题

TL;DR 在循环对象的for循环中,我不小心将指针附加到列表而不是对象本身,因此最终整个切片由多个相同的对象组成。我不知道如何修复这个问题。

详细描述

我仍然在努力弄清楚Go语言中的指针。昨天我发布了一个问题并得到了一些帮助,但现在我在同一段代码中遇到了一个稍微不同的问题。

我正在使用gocqlcqlr Go包来尝试为我的Cassandra查询构建一个小型对象映射器。实际上,我遇到的问题是,我似乎在将指针附加到对象而不是数组中的新实例。我该如何修复这个问题?我尝试在value前面添加&*,但似乎不起作用。我该如何修复这些问题?根据文档,绑定函数需要一个&

代码

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
}

文档要求var value type,然后在绑定时传递&value。我在下面引用了文档。

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

问题是,我不能直接使用var value query.structType来定义其类型,然后将其引用传递给bindQuery.Scan()

打印的内容

&{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}

切片中的内容

剧透,它是一遍又一遍地重复result5。我理解我只是将指针附加到列表中的同一个对象,并且每次循环迭代对象都会更改,这会将切片中的所有结果更改为该新对象。我只是不知道如何修复它。

[{"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"}]
英文:

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

得分: 0

好的,以下是翻译好的内容:

我至少可以告诉你在做什么。bindQuery接受一个指针。它会改变存储在该地址上的值。

你实际上在做的是这样的:

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("\n")
    for _, value := range myInts {
        fmt.Printf("%d ", *value)
    }
    fmt.Printf("\n")
    fmt.Println(myInts)
}

正如你可能猜到的那样,它会输出:

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

使用reflect会变得有些混乱。你可以将类型作为接口获取,但仅限于此(除非你想使用unsafe进行操作)。简单来说,接口包含了指向原始类型的指针(以及其他一些内容)。因此,在你的函数中,你传递了一个指针(以及其他一些内容)。然后你将该指针附加到切片中。也许直接获取具体类型并使用类型断言会更好。我假设你知道可能的类型。在这种情况下,你需要类似以下的代码:

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)
    // 获取一个新的实例
    newn := reflect.New(n).Interface()
    // 判断类型并处理每种情况
    switch t := newn.(type) {
    case **foo:
        *t = &foo{"hi!"}
        fmt.Printf("这是一个 **foo,这是地址 %p,这是值 %v\n", *t, **t)
    case **bar:
        *t = &bar{"bye :("}
        fmt.Printf("这是一个 **bar,这是地址 %p,这是值 %v\n", *t, **t)
    case *foo:
        t = &foo{"hey!"}
        fmt.Printf("这是一个 *foo,这是地址 %p,这是值 %v\n", t, *t)
    case *bar:
        t = &bar{"ahh!"}
        fmt.Printf("这是一个 *bar,这是地址 %p,这是值 %v\n", t, *t)
    default:
        panic("AHHHH")
    }
}

你也可以在循环中继续调用value = reflect.New(query.structType).Interface(),这样每次都会得到新的接口。在每次附加之后重新分配value。最后一次循环会多生成一个接口。

英文:

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 &quot;fmt&quot;

func main() {
	var q int
	myInts := make([]*int, 0, 5)
	for i := 0; i &lt; 5; i++ {
		q = i
		fmt.Printf(&quot;%d &quot;, q)
		myInts = append(myInts, &amp;q)
	}
	fmt.Printf(&quot;\n&quot;)
	for _, value := range myInts {
		fmt.Printf(&quot;%d &quot;, *value)
	}
	fmt.Printf(&quot;\n&quot;)
	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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type foo struct {
	fooval string
}

type bar struct {
	barval string
}

func main() {
	f1 := foo{&quot;hi&quot;}
	f2 := &amp;foo{&quot;hi&quot;}
	b1 := bar{&quot;bye&quot;}
	b2 := &amp;bar{&quot;bye&quot;}

	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 = &amp;foo{&quot;hi!&quot;}
		fmt.Printf(&quot;It was a **foo, here is the address %p and here is the value %v\n&quot;, *t, **t)
	case **bar:
		*t = &amp;bar{&quot;bye :(&quot;}
		fmt.Printf(&quot;It was a **bar, here is the address %p and here is the value %v\n&quot;, *t, **t)
	case *foo:
		t = &amp;foo{&quot;hey!&quot;}
		fmt.Printf(&quot;It was a *foo, here is the address %p and here is the value %v\n&quot;, t, *t)
	case *bar:
		t = &amp;bar{&quot;ahh!&quot;}
		fmt.Printf(&quot;It was a *bar, here is the address %p and here is the value %v\n&quot;, t, *t)
	default:
		panic(&quot;AHHHH&quot;)
	}
}

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..

huangapple
  • 本文由 发表于 2015年11月19日 01:20:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/33786353.html
匿名

发表评论

匿名网友

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

确定