douyun7718 2017-10-09 19:42
浏览 48
已采纳

重置循环中的指针

This question betrays my basic lack of understanding of Golang pointers (or any pointer, really), so please bear with me. I've also put a similar, working example on Go Playground if it's helpful:

https://play.golang.org/p/Xe-ZRdFWGp

Suppose that I have a basic parent/child relationship with two structs:

//Parent
type User struct{
     ID int
     Rsvps []*Rsvp
}   

//Child
type Rsvp struct{
   Response string
}

At some point, a bunch of users and RSVPs is created, and the information stored in a database. At some point, it will come time to extract information from that database and write it back into these structs. When using a relational database, I'll typically try to do it with a single query, in a pattern I've been using for many years, but which may not be the right way any more. I'll to set up a loop to extract the data. Here's some pseudocode with many comments:

func getUsersAndRsvps() []*User{

    sql := "SELECT * FROM users LEFT JOIN rsvps ON users.field1 = rsvps.field1 ORDER BY user.ID;"

    dataset := getDataset(sql)

    result = []*User{}

    rsvps = []*Rsvp{}
    //Oh, but you already see the problem here, don't you! I'm defining
    //rsvps outside of the loop, and the values contained at its address
    //will become values for all users, instead of per user. Yet, how
    //else can I collect together rsvps while iterating?

    user = User{} //hold onto a user while iterating

    lastUserID := int64(0) //track when we move from one user to the next

    for _, record := range dataset{

         thisUserID := record.ID

         //When this user is different from last user
         //take the collected rsvps and write them into 
         //the (old) user, then continue iterating...

         if lastUserID != thisUserID && lastUserID > 0{

             //So, right here is the big problem. I'm writing
             //the address of collected rsvps into the previous user record. 
             //However, on each iteration, that address gets all
             //new info, such that at the end of the readout,
             //all users have the same rsvps.
             user.Rsvps = rsvps

             result = append(result, &user)

             //So, yes, I "blank out" the rsvps, but that only goes 
             //to make the last user's rsvps be those shared among all
             rsvps = []*Rsvp{} 
         }

         //Gather rsvps
         rsvp = getRsvp(rsvp) //defined elsewhere
         rsvps = append(rsvps, &rsvp)

         user = getUser(record) //defined elsewhere

         lastUserID := thisUserID
    }

    //Capture last record
    user.Rsvps = rsvps
    result = append(result, &user)

}

To make the question succinct and hopefully clear, how do I iterate through a dataset, collecting items into a slice, and then write that slice into a unique memory point such that the next set of iterations won't overwrite it?

  • 写回答

2条回答 默认 最新

  • duanjiao4763 2017-10-09 23:41
    关注

    The problem is not caused by a pointer to Rsvp but the following statement(s):

    user := User{} //hold onto a user while iterating
    
    //... omitted for clarity
    for _, record := range dataset{
        //...   
        if lastUserID != thisUserID && lastUserID > 0{
            //... 
    
            /*--- The problem is here ---*/
            result = append(result, &user)
    
            //...
        }
        //...       
        user = getUser(record) //defined elsewhere
        //...
    }
    

    During each iteration, the value of variable user is overwritten, but since variable user is defined outside the loop, address to variable user (i.e. &user) will remain the same. As a result, the elements in result slice will be the same, i.e. address to single user variable, in which its value is captured from last record. Change the append statement to:

    //result = append(result, &user)
    u := user
    result = append(result, &u)
    

    A minimum example to demonstrate the issue can be found at The Go Playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化