分配的指针字段变为<nil>。

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

Assigned pointer field becomes <nil>

问题

我有一个结构体:

type user struct {
    Id string
    ..
    data_ptr *userData
}

我在全局范围内存储了一组用户的切片:

type Hall struct {
    users []user
}

var hall = Hall{}    // 全局变量

最后,是 HTTP 处理程序:

func dataHandler(response http.ResponseWriter, request *http.Request) {
    userExist, user_ptr := hall.haveUserId()    // 运行正常
    switch requestType {
    case "load":
        user_ptr.loadData()   // 数据加载完成,user_ptr.data_ptr 被设置
    case "newData":
        user_ptr.data_ptr = newData  // <-- 这就是问题所在,之前设置的 user_ptr.data_ptr 现在为 nil
}

那么,为什么当我发送 "load" 请求时,它会加载数据并为 user_ptr 设置 data_ptr。但是在下一次调用 "newData" 请求时,user_ptr.data_ptr 又变成了 nil

以防万一,这是 loadData() 的代码:

func (p *user) loadData(userId) {
    ..
    data := userData {}
    p.data_ptr = &data
}

编辑: user_ptr 是从哪里来的:

func (h *Hall) haveUserId(id string) (bool, *user) {
    for _, u := range h.users {
        if u.Id == id {
            fmt.Println("UID found")
            return true, &u
        }
    }
    return false, nil
}
英文:

I have a struct:

type user struct {
Id string
..
data_ptr *userData
}

And I store slice of users in global scope:

type Hall struct {
    users []user
}

var hall = Hall{}    //global

Finally, http handler:

func dataHandler(response http.ResponseWriter, request *http.Request) {
    userExist, user_ptr := hall.haveUserId()    //works fine
    switch requestType {
    case &quot;load&quot;:    
        user_ptr.loadData()   //data loaded and user_ptr.data_ptr is set
    case &quot;newData&quot;:
        user_ptr.data_ptr = newData  // &lt;-- this is it, now previously set data_ptr == nil

So, why the heck, I mean I send "load" request, it loads data, sets data_ptr for user_ptr. But on next call, "newData" request, user_ptr.data_ptr is nil?

Just in case, here is loadData():

func (p *user) loadData(userId) {
    ..
    data := userData {}
    p.data_ptr = &amp;data
}

EDIT: where user_ptr comes from:

func (h *Hall) haveUserId(id string) (bool, *user) {
	for _, u := range h.users {
		if u.Id == id {
			fmt.Println(&quot;UID found&quot;)
			return true, &amp;u
		}
	}
	return false, nil
}

答案1

得分: 5

这是因为你在操作的是一个副本,而不是切片元素本身。

在你的haveUserId()函数中,for ... range会对循环的元素进行复制,并返回该副本的地址。因此,之后你会修改这个与切片中的值无关的副本。所以,如果之后你检查切片元素的地址,它仍然不会改变(nil)。

可能的修复方法是返回切片元素的地址:&h.users[i]

func (h *Hall) haveUserId(id string) (bool, *user) {
    for i := range h.users {
        if h.users[i].Id == id {
            fmt.Println("UID found")
            return true, &h.users[i]
        }
    }
    return false, nil
}

为了证明这一点,可以看一下这个例子:

type Point struct{ x, y int }
ps := []Point{{1, 2}, {3, 4}}
fmt.Println(ps) // 输出:[{1 2} {3 4}]

for _, v := range ps {
    v.x += 10 // 仅修改副本
}
fmt.Println(ps) // 输出(未改变):[{1 2} {3 4}]

for i := range ps {
    ps[i].x += 10 // 修改切片中的值
}
fmt.Println(ps) // 输出(已改变):[{11 2} {13 4}]

Go Playground上尝试一下吧。

英文:

This is because you operate on a copy and not on the slice element itself.

In your haveUserId() function the for ... range makes a copy of the elements it loops over, and you return the address of this copy. And so later you will modify this copy which is independent from the value in the slice. So if later you check the address in the slice element, it will still be unchanged (nil).

Possible fix: return the address of the slice element: &amp;h.users[i]

func (h *Hall) haveUserId(id string) (bool, *user) {
    for i := range h.users {
        if h.users[i].Id == id {
            fmt.Println(&quot;UID found&quot;)
            return true, &amp;h.users[i]
        }
    }
    return false, nil
}

To demonstrate this, see this example:

type Point struct{ x, y int }
ps := []Point{{1, 2}, {3, 4}}
fmt.Println(ps) // Output: [{1 2} {3 4}]

for _, v := range ps {
	v.x += 10 // Modifies just the copy
}
fmt.Println(ps) // Output (unchanged): [{1 2} {3 4}]

for i := range ps {
	ps[i].x += 10 // Modifies value in slice
}
fmt.Println(ps) // Output (changed): [{11 2} {13 4}]

Try it on the Go Playground.

huangapple
  • 本文由 发表于 2016年1月13日 15:40:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/34760950.html
匿名

发表评论

匿名网友

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

确定