for ...range前端
最近兩週用Golang作項目,編寫web服務,兩週時間寫了大概五千行代碼(業務代碼加單元測試用例代碼)。用Go的感受很爽,編碼效率高,運行效率也不錯,用了beego,avro,xorm,反射。今天和前端聯調遇到了一個bug,發現踩到了第二個坑。踩坑不怕,踩過一次就不會再犯了,這就是實踐的好處。mysql
坑是這樣的:數據採用avro描述,用xorm存取到mysql;對於有嵌套的數據結構,avro生成的go結構體以指針切片的形式聲明,xorm Find方法採用告終構體切片的形式。有以下avro schema:golang
{web
"fields": [sql
{json
"name": "Oid",數據結構
"type": "int"app
},ide
{函數
"name": "name",
"type": "string"
},
{
"name": "inner_objs",
"type": {
"items": {
"fields": [
{
"name": "Iid",
"type": "int"
},
{
"name": "name",
"type": "string"
},
{
"name": "degree",
"type": "int"
}
],
"name": "inner_obj",
"type": "record"
},
"type": "array"
}
}
],
"name": "outer_obj",
"type": "record"
}
對應的go結構體及數據操做函數以下:
package models
type OuterObj struct {
Oid int32 `json:"oid" xorm:"pk notnull"`
Name string `json:"name"`
InnerObjs []*InnerObj `json:"inner_objs"`
}
type InnerObj struct {
Oid int32 `json:"-" xorm:"pk notnull"`
Iid int32 `json:"iid" xorm:"pk notnull"`
Name string `json:"name"`
Degree int32 `json:"degree"`
}
func GetOuterObjs(index int, count int) (objs []OuterObj, err error) {
objs = make([]OuterObj, 0)
err = x_intellitbi.Asc("oid").Limit(count, (index-1)*count).Find(&objs)
if err != nil {
return
}
for index := 0; index < len(objs); index++ {
objs[index].InnerObjs = make([]*InnerObj, 0)
innerObjs := make([]InnerObj, 0)
err = x_intellitbi.Where("oid=?", objs[index].Oid).Find(&innerObjs)
if err != nil {
return
}
for _, v := range innerObjs {
objs[index].InnerObjs = append(objs[index].InnerObjs, &v)
}
}
Return
}
GetOuterObjs返回的結果不符合預期,每一個OuterObj實例內的InnerObjs對應的內容所有相同。
潛意識裏,確定有一部分人對帶有短變量聲明(short variable declaration :=)的for...range的理解是:每次迭代聲明一個不一樣的變量。用Google搜了下,在2011年golang-nuts上確實有過關於該問題的討論(https://groups.google.com/forum/#!topic/golang-nuts/e08r1Vk7ufQ)。
最新的spec中有說明:
The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement; they are re-used in each iteration. If the iteration variables are declared outside the "for" statement, after execution their values will be those of the last iteration.
翻譯以下:
迭代變量能夠由「range」子句使用一個短變量聲明(:=)形式聲明。在這種狀況下,它們的類型設置爲相應迭代值的類型,其範圍是「for」語句的塊;它們在每次迭代中被重用。若是迭代變量在「for」語句以外被聲明,執行後它們的值將是上一次迭代的值。
用:=聲明的迭代變量在每次迭代過程當中被重用了,應該能夠理解成在for做用域內聲明瞭迭代變量,在for內可見,每次迭代過程當中被從新評估值;與在for外部聲明迭代變量相似,區別是做用域不一樣。
另外再說下xorm,InsertMulti採用了指針切片,Find採用告終構體切片的指針,若是Find統一成指針切片就更好了,這樣也省得多一層轉換。