2017-10-09 5 views
1

この質問はGolangポインタ(または実際にはポインタ)の私の基本的な欠如を裏切っています。私それは便利です場合も行く遊び場に類似し、作業例を入れている:いくつかの時点でループ内のポインタをリセットする

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

//Child 
type Rsvp struct{ 
    Response string 
} 

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

を私は2つの構造体の基本的な親/子関係を持っていると仮定多数のユーザとRSVPが作成され、情報がデータベースに格納されます。ある時点で、そのデータベースから情報を抽出し、これらの構造体に書き戻す時間が来るでしょう。リレーショナルデータベースを使用する場合、私は長年使用してきたパターンで、通常は単一のクエリでそれを実行しようとしますが、もう正しい方法ではない可能性があります。私はデータを抽出するためのループを設定します。ここでは多くのコメントといくつかの擬似コードです:

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) 

} 

質問は簡潔でうまくいけば明確にするために、どのように私はスライスにアイテムを収集し、データセットを反復処理します、そして、次に、そのユニークなメモリポイントにそのスライスを書きます次の反復のセットはそれを上書きしませんか?

+0

すべての変数は、それぞれのメモリに書き込まれます。ループのスコープ(または任意のブロック)の外に変数を残したい場合は、変数をそのブロックの外に宣言します。 – Adrian

+0

はい。私はあなたのソリューションを誤解していない限り、私がやっていることだと思います。ポインタのスライス変数をループスコープの外に設定し、各繰り返しで書き換えられるのを見るだけです。 – Brent

+0

どの変数が上書きされていますか? – Adrian

答えて

1

:各反復中

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

、変数userの値が上書きされるが、可変userので、ループの外側で定義されています、変数user(つまり&user)へのアドレスは同じままです。結果として、resultスライスの要素は同一であり、すなわちアドレスuser変数へのアドレスであり、その値は最後のレコードから取得されます。問題を実証するための最低限の例がThe Go Playgroundで見つけることができます

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

:にappendステートメントを変更します。

+0

良い天気、どのような "簡単な"答え。ありがとうございました!変数を別の「新鮮な」変数と同じに設定するだけで、その変数を保持してからスライスに追加します。ブリリアント! – Brent

+0

一方、私はMCVEを準備していましたが、誰かが今学問的興味を持っている場合に備えて、https://play.golang.org/p/yx_SUVXaRe – Brent

0

はどのようにして次の操作を行いについて:

package main 

    import (
     "fmt" 
    ) 

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

    type Rsvp struct { 
     Response string 
    } 

    func main() { 
     users := []int{1, 2, 3} 
     responses := []string{"Yes", "No", "Maybe"} 
     var results []*User 
     for _, i := range users { 
      r := Rsvp{Response: responses[i-1]} // create new variable 
      u := User{ID: i} 
      u.Rsvps = append(u.Rsvps, &r) 
      results = append(results, &u) 
     } 
     for _, r := range results { 
      fmt.Println(r.ID, r.Rsvps[0].Response) 
     } 

    } 

私はあなたの遊び場の例をとって、コメントを剥離し、ご希望の出力を得るために、コードを変更しました。主な変更は私がrを再利用しないことです。当初は、&rを常に追加していましたが、ループの開始時にはrを変更していました。もちろん、これはrが指すメモリを変更し、すべてをMaybeにします。問題はRsvpへのポインタが、次の文(S)によって引き起こされていない

+0

ありがとうございます。遊び場の例では、私はユーザーのために複数のRSVPを収集しようとしているという問題を捉えていませんでした。ループの前にスライスを定義し、ループ中にユーザのすべてのRSVPをまとめて収集し、ループが新しいユーザにヒットしたときに収集したRSVPをユーザに割り当てる必要があります。 – Brent

関連する問題