Perché l'assegnazione di un'interfaccia è più rigorosa di una chiamata al metodo?

Aug 20 2020

Sto attraversando il Tour of Go. Ho imparato che se abbiamo un metodo che accetta un puntatore come ricevitore, accetterà anche un tipo di valore come ricevitore (con go che esegue la conversione automaticamente).

type Vertex struct { X, Y float64 }

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

Quindi il codice seguente è valido, indipendentemente dal fatto che Vertex venga ricevuto da valore o puntatore

v := Vertex{1, 2}
fmt.Println(v.Abs())

p := &v
fmt.Println(p.Abs())

Tuttavia, supponiamo di avere la seguente interfaccia:

type Abser interface {
    Abs() float64
}

Allora, perché il seguente codice non è valido?

var a Abser
v := Vertex{1, 2}

a = v // invalid

La mia comprensione era che questo sarebbe andato bene. Anche se v è un tipo di valore che "implementa" la funzione Abs che accetta un ricevitore di puntatore, lo prenderebbe anche per valore?

Le interfacce erano semplicemente progettate per essere più rigorose in termini di ciò che una variabile di interfaccia può contenere sul lato destro? L'interfaccia vede * Vertex e Vertex come due tipi diversi, tuttavia, neanche il metodo Abs () ha problemi di elaborazione.

Risposte

3 Marc Aug 20 2020 at 12:32

In entrambi i casi, Go desidera un puntatore su cui eseguire il metodo. La grande differenza è che le chiamate ai metodi prenderanno automaticamente l'indirizzo di v, ma controllare se qualcosa implementa un'interfaccia non lo fa.

Chiamate al metodo:

Quando si chiama un metodo con ricevitore puntatore su un tipo normale, Go prenderà automaticamente l'indirizzo se consentito. Dalle specifiche sulle chiamate al metodo (l'evidenziazione è mia):

Una chiamata al metodo xm () è valida se il set di metodi di (il tipo di) x contiene m e l'elenco di argomenti può essere assegnato all'elenco di parametri di m. Se x è indirizzabile e il set di metodi di & x contiene m, xm () è l'abbreviazione di (& x) .m ()

In questo caso, xfa riferimento alla tua variabile ved è indirizzabile . Quindi, nelle chiamate al metodo, Go viene eseguito automaticamente (&v).Abs().

Incarico:

Quando si tenta di assegnare a = v , il controllo che deve essere riempito è T is an interface type and x implements T.. vimplementa Absersolo se il suo set di metodi corrisponde all'interfaccia. Questo set di metodi è determinato come segue:

Il metodo set di qualsiasi altro tipo T è costituito da tutti i metodi dichiarati con tipo di ricevitore T . L'insieme di metodi del tipo di puntatore corrispondente * T è l'insieme di tutti i metodi dichiarati con il ricevitore * T o T (cioè, contiene anche l'insieme di metodi di T).

Noterai che quando calcola il metodo impostato, Go non prende l'indirizzo di vcome faceva nella chiamata al metodo. Ciò significa che il metodo impostato per var v Vertexè vuoto, non riuscendo a implementare l'interfaccia.

Soluzione:

Il modo per aggirare questo è prendere l'indirizzo di vte stesso:

var a Abser
v := Vertex{1, 2}

a = &v

In questo modo, ora stai esaminando il set di metodi *Vertexche include Abs() float64e quindi implementa l'interfaccia Abser.