2016-07-03 18 views
7

Sono di 2 giorni nel mondo di Golang e sto passando per il go tour. Non ho potuto fare a meno di notare una particolarità che non riesco a trovare in termini corretti.Perché non posso assegnare il valore del tipo a un'interfaccia che implementa metodi con il puntatore del tipo di ricevitore per quel tipo?

Questo codice viene eseguito alla perfezione:

package main 
import (
    "fmt" 
    "math" 
) 
type Vertex struct{ 
    X,Y float64 
} 
type Abser interface{ 
    Abs() float64 
} 
func (v Vertex) Abs() float64{ //method with value receiver argument 
    return math.Sqrt(v.X*v.X+v.Y*v.Y) 
} 
func main(){ 
    var myVer Vertex = Vertex{3,4} 
    var inter Abser 
    inter = &myVer //assigning *Vertex type to inter 
    fmt.Println(inter.Abs()) 
} 

Nel frattempo, il codice seguente mostra un errore:

package main 
import (
    "fmt" 
    "math" 
) 
type Vertex struct{ 
    X,Y float64 
} 
type Abser interface{ 
    Abs() float64 
} 
func (v *Vertex) Abs() float64{ //method with pointer receiver argument 
    return math.Sqrt(v.X*v.X+v.Y*v.Y) 
} 
func main(){ 
    var myVer Vertex = Vertex{3,4} 
    var inter Abser 
    inter = myVer //assigning Vertex type to inter 
    fmt.Println(inter.Abs()) 
} 

L'errore è:

interface.go:18: cannot use myVer (type Vertex) as type Abser in assignment: Vertex does not implement Abser (Abs method has pointer receiver)

fino ad arrivare a questa sezione del tour, ho potuto capire che i creatori di Go hanno abbandonato le notazioni ingombranti come

(*v).method1name()

(&v).method2name()

In modo che i metodi con i destinatari del valore possano essere utilizzati con valori e puntatori e viceversa.

Perché la lingua discrimina tra i due (valore e puntatore) quando si lavora con le interfacce? Non sarebbe più conveniente se qui si applicassero gli stessi principi di riferimento/dereferenziazione?

Spero di non mancare qualcosa di troppo apparente. Grazie!

+0

Sono venuto qui per discutere lo stesso esempio. Non riesco a capirlo. È qui: https://tour.golang.org/methods/9 –

risposta

4

"Intro++ to Go Interfaces" illustra il problema:

*Vertex is a type. It’s the “pointer to a Vertex ” type. It’s a distinct type from (non-pointer) Vertex . The part about it being a pointer is part of its type.

Hai bisogno di coerenza di tipo.

"Methods, Interfaces and Embedded Types in Go":

The rules for determining interface compliance are based on the receiver for those methods and how the interface call is being made.
Here are the rules in the spec for how the compiler determines if the value or pointer for our type implements the interface :

  • Il metodo set del corrispondente tipo di puntatore *T è l'insieme di tutti i metodi con ricevitore *T o T

This rule is stating that if the interface variable we are using to call a particular interface method contains a pointer, then methods with receivers based on both values and pointers will satisfy the interface.

  • Procedimento set di qualsiasi altro tipo T consiste di tutti i metodi con il tipo di ricevitore T.

This rule is stating that if the interface variable we are using to call a particular interface method contains a value, then only methods with receivers based on values will satisfy the interface.

Karrot Kake s' answer merito method set è dettagliata anche in go wiki:

The method set of an interface type is its interface.

The concrete value stored in an interface is not addressable, in the same way that a map element is not addressable.
Therefore, when you call a method on an interface, it must either have an identical receiver type or it must be directly discernible from the concrete type.

Pointer- and value-receiver methods can be called with pointers and values respectively, as you would expect.
Value-receiver methods can be called with pointer values because they can be dereferenced first.
Pointer-receiver methods cannot be called with values, however, because the value stored inside an interface has no address.

("ha nessun indirizzo" significa in realtà che è not addressable)

+1

[Intro ++ to Go Interfaces] (http://npf.io/2014/05/intro-to-go-interfaces/) ha risolto il mio dubbio abbastanza chiaramente . Dichiara chiaramente che: "..valore in un'interfaccia è in una posizione di memoria nascosta, e quindi il compilatore non può automaticamente ottenere un puntatore a quella memoria per voi (in Go parlance, questo è noto come "non indirizzabile"). " Vorrei suggerire a chiunque abbia di fronte gli stessi dubbi che ho di fronte, di visitare il link e di esaminarlo una volta, ne varrà la pena. @VonC, grazie per i link meravigliosi :-) –

+1

@TanmayGarg true, ho dettagliato il non parte indirizzabile nella mia ultima modifica della risposta, alla fine – VonC

+0

Ahh sì. Grazie, ancora una volta :-) –

3

Il method set per un puntatore del tipo include lo metodi di ricezione del valore, ma il metodo impostato per un valore non include i metodi del ricevitore puntatore.Il metodo impostato per un tipo di valore non include i metodi di ricezione del puntatore in quanto i valori memorizzati in un'interfaccia non sono addressable.

2

bella domanda.
questo è Golang tipo di sistema: il Vertex e *Vertex sono diversi tipi,
vedono questo campione chiarire:
è possibile utilizzare type Vertex2 Vertex per definire nuovi Vertex2 tipo e questi sono i diversi tipi troppo:

mentre questo è di lavoro:

package main 

import "fmt" 
import "math" 

func main() { 
    var myVer Vertex = Vertex{3, 4} 
    var inter Abser = myVer //assigning Vertex type to inter 
    fmt.Println(inter.Abs()) 
} 

func (v Vertex) Abs() float64 { //method with pointer receiver argument 
    return math.Sqrt(v.X*v.X + v.Y*v.Y) 
} 

type Vertex struct { 
    X, Y float64 
} 
type Abser interface { 
    Abs() float64 
} 

questo non funzionerà:

package main 

import "fmt" 
import "math" 

func main() { 
    var myVer Vertex = Vertex{3, 4} 
    var inter Abser = myVer //assigning Vertex type to inter 
    fmt.Println(inter.Abs()) 
} 

type Vertex2 Vertex 

func (v Vertex2) Abs() float64 { //method with pointer receiver argument 
    return math.Sqrt(v.X*v.X + v.Y*v.Y) 
} 

type Vertex struct { 
    X, Y float64 
} 
type Abser interface { 
    Abs() float64 
} 

l'errore è:

\ M.Go:. 8: non può usare myVer (tipo Vertex) come tipo Abser nell'assegnazione:
Vertex non implementa Abser (metodo Abs mancante)

esattamente lo stesso errore con il campione:

package main 

import "fmt" 
import "math" 

func main() { 
    var myVer Vertex = Vertex{3, 4} 
    var inter Abser = myVer //assigning Vertex type to inter 
    fmt.Println(inter.Abs()) 
} 

func (v *Vertex) Abs() float64 { //method with pointer receiver argument 
    return math.Sqrt(v.X*v.X + v.Y*v.Y) 
} 

type Vertex struct { 
    X, Y float64 
} 
type Abser interface { 
    Abs() float64 
} 

perché i due vertici e Vertex2 sono diversi tipi.
e questo funziona anche:

package main 

import "fmt" 
import "math" 

func main() { 
    var inter Abser = &Vertex{3, 4} //assigning &Vertex type to inter 
    fmt.Printf("inter: %#[1]v\n", inter) 
    fmt.Println(inter.Abs()) 
} 

func (v Vertex) Abs() float64 { //method with pointer receiver argument 
    return math.Sqrt(v.X*v.X + v.Y*v.Y) 
} 

type Vertex struct { 
    X, Y float64 
} 
type Abser interface { 
    Abs() float64 
} 

uscita:

inter: &main.Vertex{X:3, Y:4} 
5 

perché:
Un tipo può avere una serie metodo associato. La serie di metodi di un tipo di interfaccia è la sua interfaccia. L'insieme di metodi di qualsiasi altro tipo T è costituito da tutti i metodi dichiarati con il tipo di ricevente T. Il set di metodi del tipo di puntatore corrispondente * T è l'insieme di tutti i metodi dichiarati con ricevitore * T o T (cioè contiene anche il set di metodi di T). Ulteriori regole si applicano alle strutture contenenti campi anonimi, come descritto nella sezione sui tipi di struttura. Qualsiasi altro tipo ha un set di metodi vuoto. In un set di metodi, ogni metodo deve avere un nome di metodo non vuoto univoco.

Il set di metodi di un tipo determina le interfacce che il tipo implementa e i metodi che possono essere chiamati utilizzando un ricevitore di quel tipo.
rif: https://golang.org/ref/spec#Method_sets

0

I valori memorizzati in un'interfaccia non sono indirizzabili, ma perché? Vedi here per la risposta.

tl; dr suo perché un puntatore ad A che punta a un valore di tipo A in un'interfaccia sarebbe invalidato quando un valore di diverso tipo B viene successivamente memorizzato nell'interfaccia.