【问题标题】:How can I dynamically populate a struct?如何动态填充结构?
【发布时间】:2020-01-06 02:04:43
【问题描述】:

我想动态填充我的内部结构,用于原子插入。我是新手,所以指针和引用它们是我仍在学习的东西。我不明白为什么每个循环都将相同的字段放入两次。我尝试删除'&'然后我得到一个不能使用类型作为*类型的错误,我检查以确保我的循环击中了tradeArray中的每个对象,并且确实如此。看起来它正在用它循环的最后一个对象覆盖它之前的对象。我该如何解决这个问题?

   func createTrade(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")

        var tradeArray []Trade
    
    
        if err := json.NewDecoder(r.Body).Decode(&tradeArray); err != nil {
            e := Error{Message: "Bad Request - Improper Types Passed"}
            w.WriteHeader(http.StatusBadRequest)
            _ = json.NewEncoder(w).Encode(e)
            return
        }
    
    
        for _, trade := range tradeArray {
            internal := InternalTrade{
                Id: strconv.Itoa(rand.Intn(1000000)),
                Trade: &trade,
            }
            submit := TradeSubmitted{
                TradeId:       internal.Id,
                ClientTradeId: trade.ClientTradeId ,
            }
            submitArray = append(submitArray, submit)
            trades = append(trades, internal)
        }
    
    
        if err := json.NewEncoder(w).Encode(submitArray); err != nil {
            e := Error{Message:"Internal Server Error"}
            w.WriteHeader(http.StatusInternalServerError)
            _ = json.NewEncoder(w).Encode(e)
            return
        }
    }

编辑:我能够通过创建一个新变量来保存交易并在结构创建中引用该变量来解决我的问题。如果有人能解释一下,我将不胜感激,我不确定这与我上面提到的“交易”有何不同。

    for _, trade := range tradeArray {
    p := trade
    internal := InternalTrade{
        Id: strconv.Itoa(rand.Intn(1000000)),
        Trade: &p,
    }
    submit := TradeSubmitted{
        TradeId:       internal.Id,
        ClientTradeId: trade.ClientTradeId ,
    }
    submitArray = append(submitArray, submit)
    trades = append(trades, internal)
}

【问题讨论】:

  • 程序中有一个变量trade,该变量的地址不变。在第二个 sn-p 中,循环的每次迭代都会创建一个新变量 p。每个新变量都有一个唯一的地址。

标签: go


【解决方案1】:

让我们看看这些部分:

    var tradeArray []Trade

    // code that fills in `tradeArray` -- correct, and omitted here

    for _, trade := range tradeArray {
        internal := InternalTrade{
            Id: strconv.Itoa(rand.Intn(1000000)),
            Trade: &trade,
        }
        submit := TradeSubmitted{
            TradeId:       internal.Id,
            ClientTradeId: trade.ClientTradeId ,
        }
        submitArray = append(submitArray, submit)
        trades = append(trades, internal)
    }

正如您所见,这个for 循环按您想要的方式工作。这是它的一个变体,有点相似,只是变量 trade 的范围超出了 for 循环:

    var trade Trade
    for i := range tradeArray {
        trade = tradeArray[i]
        internal := InternalTrade{
            Id: strconv.Itoa(rand.Intn(1000000)),
            Trade: &trade,
        }
        // do correct stuff with `internal`
    }

请注意,每个internal 对象都指向一个单个共享 trade 变量,其值在每次循环时都会被覆盖。结果是它们都指向last循环中的那个。

您的修复本身是可以的:每次通过循环,您创建一个 new(不同的)p 变量,并使用&p,这样每个internal.Trade 都有一个不同的指针到不同的副本。您也可以在循环内执行trade := trade,以创建一个新的唯一trade 变量。然而,在这种特定情况下,以这种方式重写循环可能是最有意义的:

    for i := range tradeArray {
        internal := InternalTrade{
            Id: strconv.Itoa(rand.Intn(1000000)),
            Trade: &tradeArray[i],
        }
        // do correct stuff with `internal`
    }

也就是说,您已经拥有len(tradeArray) 不同 Trade 对象:切片标头tradeArray 允许您访问存储在底层数组中的每个tradeArray[i] 实例。你可以直接指向那些。

这种方法有各种优点和缺点。最大的优势是您根本不需要重新复制每笔交易:您只需使用切片标头覆盖的数组中的那些,它们在 json Decode 函数中的某处分配。最大的缺点是,只要您保留 any 指向其元素的 any 的指针,该底层数组就不能被垃圾收集。取决于剩余代码的结构,该劣势可能完全没有成本,但如果它劣势,请考虑将tradeArray 声明为:

var tradeArray []*Trade

这样jsonDecode 函数会分别分配每一个,您可以一次指向一个,而不会强制保留整个集合。

【讨论】:

  • 哇,答案真棒,谢谢!我更喜欢你的解决方案,它看起来更干净。
猜你喜欢
  • 1970-01-01
  • 2015-12-10
  • 2015-04-25
  • 1970-01-01
  • 2015-04-05
  • 2011-08-13
  • 2023-03-21
  • 1970-01-01
相关资源
最近更新 更多