首页 > 解决方案 > 如何动态填充结构?

问题描述

我想动态填充我的内部结构,用于原子插入。我是新手,所以指针和引用它们是我仍在学习的东西。我不明白为什么每个循环都将相同的字段放入两次。我尝试删除'&'然后我得到一个不能使用类型作为*类型的错误,我检查以确保我的循环击中了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)
}

标签: go

解决方案


让我们看看这些部分:

    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变量,其值在每次循环时都会被覆盖。结果是它们都指向最后一次循环中的那个。

您的修复本身是可以的:每次循环遍历,您创建一个的(不同的)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函数内部的某个地方分配。最大的缺点是,只要您保留指向其任何元素的任何指针,就无法对该底层数组进行垃圾收集。根据剩余代码的结构,该缺点可能根本没有成本,但如果它缺点,请考虑声明为:tradeArray

var tradeArray []*Trade

这样该json Decode函数会分别分配每一个,并且您可以一次指向它们一个,而不会强制保留整个集合。


推荐阅读