英文:
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 "load":
user_ptr.loadData() //data loaded and user_ptr.data_ptr is set
case "newData":
user_ptr.data_ptr = newData // <-- 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 = &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("UID found")
return true, &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: &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
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论