2

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
}
icza
  • 389,944
  • 63
  • 907
  • 827
Dennis S
  • 315
  • 3
  • 13
  • Most likely because you operate on a _copy_ and not on the slice element. How do you create `user_ptr`? – icza Jan 13 '16 at 07:46
  • Can you please `gofmt` and show your actual code, ie where `user_ptr` comes from? – user3591723 Jan 13 '16 at 07:47
  • @icza Updated, please see edit at the end of question – Dennis S Jan 13 '16 at 07:57
  • @user3591723, question updated with relevant piece – Dennis S Jan 13 '16 at 07:58
  • @DensiTensy you haven't added enough relevant information and still no `gofmt`. Just show your entire `dataHandler` function as it is actually written in your code. This can't possibly be it; there would be compile errors everywhere. – user3591723 Jan 13 '16 at 08:01

1 Answers1

5

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.

icza
  • 389,944
  • 63
  • 907
  • 827