2016-01-26 2 views
5

Ich habe gekämpft, um den Unterschied zwischen Kodierung/Dekodierung eines Schnittstellentyps zu verstehen, wenn dieser Typ in einer Struktur eingebettet ist, vs. wenn es überhaupt nicht eingebettet ist.Unterschied zwischen Gob Dekodierung einer Schnittstelle in einer Struktur, vs. rohe

Mit dem folgenden Beispiel: here in the playground

Hinweis erklärt der Code eine Schnittstelle IFace. Es deklariert eine nicht exportierte Struktur impl. Es richtet einige Gob-Methoden zu Register, GobEncode und GobDecode die impl Struktur ein.

Dann deklariert es auch eine Struktur Data, die exportiert wird und ein einzelnes Feld Foo hat, das vom Schnittstellentyp IFace ist.

Also gibt es eine Schnittstelle, eine Struktur, die es implementiert, und eine Container-Struktur, die ein Feld hat, dessen Wert dieser Schnittstellentyp ist.

Mein Problem ist, dass die Container-Struktur Data glücklich durch den Gob Gauntlet gesendet wird, und wenn es durchläuft, kodiert es glücklich und entschlüsselt das IFace-Feld in der Data Struktur ... großartig! Aber ich kann nicht in der Lage sein, nur eine Instanz des IFace-Wertes durch den Gob Gauntlet zu senden.

Was ist die magische Invokation, die ich vermisse?

Die Suche nach der Fehlermeldung gibt eine Reihe von Ergebnissen, aber ich glaube, ich habe den GOB-Vertrag erfüllt .... und der "Beweis" davon ist in der erfolgreichen Struktur-Gobbing. Offensichtlich habe ich etwas übersehen, kann es aber nicht sehen.

Note, die Ausgabe des Programms ist:

Encoding {IFace:bilbo} now 
Encoding IFace:baggins now 
Decoded {IFace:bilbo} now 
decode error: gob: local interface type *main.IFace can only be decoded from remote interface type; received concrete type impl 
Decoded <nil> now 

Der tatsächliche Code ist:

package main 

import (
    "bytes" 
    "encoding/gob" 
    "fmt" 
) 

type IFace interface { 
    FooBar() string 
} 

type impl struct { 
    value string 
} 

func init() { 
    gob.Register(impl{}) 
} 

func (i impl) FooBar() string { 
    return i.value 
} 

func (i impl) String() string { 
    return "IFace:" + i.value 
} 

func (i impl) GobEncode() ([]byte, error) { 
    return []byte(i.value), nil 
} 

func (i *impl) GobDecode(dat []byte) error { 
    val := string(dat) 
    i.value = val 
    return nil 
} 

func newIFace(val string) IFace { 
    return impl{val} 
} 

type Data struct { 
    Foo IFace 
} 

func main() { 

    var network bytes.Buffer  // Stand-in for a network connection 
    enc := gob.NewEncoder(&network) // Will write to network. 
    dec := gob.NewDecoder(&network) // Will read from network. 

    var err error 

    var bilbo IFace 
    bilbo = newIFace("bilbo") 

    var baggins IFace 
    baggins = newIFace("baggins") 

    dat := Data{bilbo} 

    fmt.Printf("Encoding %v now\n", dat) 
    err = enc.Encode(dat) 
    if err != nil { 
     fmt.Println("encode error:", err) 
    } 

    fmt.Printf("Encoding %v now\n", baggins) 
    err = enc.Encode(baggins) 
    if err != nil { 
     fmt.Println("encode error:", err) 
    } 

    var pdat Data 
    err = dec.Decode(&pdat) 
    if err != nil { 
     fmt.Println("decode error:", err) 
    } 
    fmt.Printf("Decoded %v now\n", pdat) 

    var pbag IFace 
    err = dec.Decode(&pbag) 
    if err != nil { 
     fmt.Println("decode error:", err) 
    } 
    fmt.Printf("Decoded %v now\n", pbag) 

} 
+0

http: // Stackoverflow.com/questions/32428797/go-rpc-Aufruf-ist-fein-lokal-Marshalling-Problem-wenn-rpc verwenden/32430429 # 32430429 –

+0

@JiangYD - dass Unmarshal-Methode ist für "Encoding/Json". Gibt es eine Entsprechung für 'encoding/gob'? – rolfl

+0

https://golang.org/pkg/encoding/gob/#GobDecoder –

Antwort

3

Der Anruf

err = enc.Encode(baggins) 

gibt einen impl Wert Encode. Es übergibt keinen Wert vom Typ IFace. Das Dokument http://research.swtch.com/interfaces kann hilfreich sein, um zu verstehen, warum dies so ist. Der Wert ist als konkreter Typ impl codiert.

Wenn Sie zu einem Schnittstellentyp dekodieren möchten, müssen Sie einen Schnittstellentyp codieren. Eine Möglichkeit, dies zu tun ist, einen Zeiger auf die Schnittstelle Wert zu übergeben:

err = enc.Encode(&baggins) 

In diesem Aufruf wird ein *IFace geben wird zu kodieren. Nach dem Dereferenzieren des Zeigers erkennt der Encoder, dass der Wert ein Schnittstellentyp ist, und codiert diesen als Schnittstellentyp. Da das Gob-Paket beim Konvertieren von Werten alle erforderlichen Dereferenzierungs- und Indirektionsfunktionen ausführt, erfordert die zusätzliche Dereferenzierungsebene beim Aufruf von Encode beim Dekodieren keine zusätzliche Dereferenzierungsebene.

playground example

+0

Also, wenn eine "rohe" Schnittstelle "Instanz" zu kodieren, ein einfaches '&' vor der Variable ist die Magie, ich schätze den Link zu diesem Interface-Dokument auch, Lesen Sie es immer noch. Ich erkläre die Forderung, die Schnittstelle bei der Codierung doppelt zu deneferenzieren, als "knifflig", besonders wenn das Symptom des Problems zur Dekodierzeit gesehen wird, nicht die Zeit kodieren. Ich denke, wenn ich mir den tatsächlich kodierten Stream ansehe, sollte ich den Unterschied sehen. – rolfl

Verwandte Themen