......它讓我發瘋,試圖了解我做錯了什么!
游樂場:https : //go.dev/play/p/ZQP8Y-gwihQ
這個例子看起來很人為,但它是從我有錯誤出現的代碼中提取的。在我的代碼中,我正在對位元組緩沖區進行哈希處理,并希望該程序是可預測的。
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type Foo struct {
Bar string
Baz string
}
func (f *Foo) X() string {
var b bytes.Buffer
s := struct {
Bar string
Baz string
}{
f.Bar,
f.Baz,
}
log.Printf("%v", s)
gob.NewEncoder(&b).Encode(s)
return fmt.Sprintf("%x", b)
}
func (f *Foo) Y(x string) string {
var b bytes.Buffer
s := struct {
Bar string
Baz string
S string
}{
f.Bar,
f.Baz,
x,
}
log.Printf("%v", s)
gob.NewEncoder(&b).Encode(s)
return fmt.Sprintf("%x", b)
}
func main() {
a := &Foo{
Bar: "bar",
Baz: "baz",
}
log.Println(a.X())
log.Println(a.Y("something"))
}
運行產量:
{bar baz}
{1cff81030102ff820001020103426172010c00010342617a010c0000000dff820103626172010362617a00 0 0}
{bar baz something}
{22ff83030102ff840001030103426172010c00010342617a010c00010153010c00000018ff840103626172010362617a0109736f6d657468696e6700 0 0}
注釋掉log.Println(a.X())產量:
{bar baz something}
{22ff81030102ff820001030103426172010c00010342617a010c00010153010c00000018ff820103626172010362617a0109736f6d657468696e6700 0 0}
我希望這兩種編碼相同,但它們在我假設對應于欄位邊界的位置上(可預測)不同:
22
ff83 # 81
030102
ff84 # 82
0001030103426172010c00010342617a010c00010153010c00000018
ff84 # 82
0103626172010362617a0109736f6d657468696e6700
即使細節不同,行為也與我的代碼一致。
我正在創建一個新的bytes.Buffer和gob.NewEncoder在每個方法中,所以不清楚為什么呼叫會X改變Y.
uj5u.com熱心網友回復:
您缺少的是,除了每個狀態之外,Encoder實體生成的位元組流還具有全域(程式范圍)Encoder狀態。該全域狀態由 [注意:此處編輯的短語] 注冊和發送型別組成。
當你發送一個型別化的值時,如果該型別在發送之前還沒有被注冊,它會在全域狀態下為你注冊。這會為該型別分配一個內部數值。請參閱Register(及其伴侶RegisterName)。當您呼叫您的 時X,它會注冊保存s在X. 當您呼叫您的 時Y,它會注冊保存s在Y. 這些獲得不同的內部型別編號。通過不呼叫X,該型別永遠不會注冊,并且Y的型別會在第一個可用數字下注冊。
在我的代碼中,我正在對位元組緩沖區進行哈希處理...
由于現在可能是顯而易見的原因,這不是一個好主意。 但是,如果您以已知順序顯式注冊每種型別,那么您在這里就足夠安全了,除非將來的某個版本出于某些(可能是好的)原因更改了有線格式。 糟糕,測驗這表明它也無濟于事。這是因為即使型別被注冊,它也沒有設定傳輸編號,直到第一次對該型別的值進行編碼。因此,您需要對每種型別的值進行編碼(然后可以選擇丟棄)。
這是一個仔細丟棄編碼這兩種型別的功能示例,以便注釋掉log.Println(a.X())對第二個值的編碼沒有影響。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/393691.html
上一篇:在goapi中獲取錯誤訊息和結果
