Perché non posso assegnare un * Struct a un'interfaccia *?


142

Sto solo lavorando attraverso il tour Go e sono confuso su puntatori e interfacce. Perché questo codice Go non viene compilato?

package main

type Interface interface {}

type Struct struct {}

func main() {
    var ps *Struct
    var pi *Interface
    pi = ps

    _, _ = pi, ps
}

cioè se Structè un Interface, perché non dovrebbe *Structessere un *Interface?

Il messaggio di errore che ricevo è:

prog.go:10: cannot use ps (type *Struct) as type *Interface in assignment:
        *Interface is pointer to interface, not interface


sembra che le interfacce potrebbero comportarsi come puntatori impliciti ...
Victor

posso suggerire di arricchire il tuo parco giochi func main() { var ps *Struct = new(Struct) var pi *Interface var i Interface i = ps pi = &i fmt.Printf("%v, %v, %v\n", *ps, pi, &i) i = *ps fmt.Printf("%v, %v, %v\n", *ps, pi, i) _, _, _ = i, pi, ps }e fare le tue conclusioni
Victor,

Risposte:


183

Quando si dispone di una struttura che implementa un'interfaccia, un puntatore a quella struttura implementa automaticamente anche quella interfaccia. Ecco perché non hai mai *SomeInterfacenel prototipo di funzioni, in quanto ciò non aggiungerebbe nulla SomeInterfacee non hai bisogno di un tale tipo nella dichiarazione di variabili (vedi questa domanda correlata ).

Un valore di interfaccia non è il valore della struttura concreta (poiché ha una dimensione variabile, questo non sarebbe possibile), ma è una specie di puntatore (per essere più precisi un puntatore alla struttura e un puntatore al tipo ). Russ Cox lo descrive esattamente qui :

I valori dell'interfaccia sono rappresentati come una coppia di due parole che fornisce un puntatore alle informazioni sul tipo memorizzato nell'interfaccia e un puntatore ai dati associati.

inserisci qui la descrizione dell'immagine

Questo è il motivo Interface, e non *Interfaceè il tipo corretto per contenere un puntatore a un'implementazione della struttura Interface.

Quindi devi semplicemente usare

var pi Interface

8
OK, penso che abbia senso per me. Mi sto solo chiedendo perché (in quel caso), non è semplicemente un errore di compilazione da dire var pi *Interface.
Simon Nickerson,

1
Sono andato in maggiori dettagli per spiegarlo. Vedi modifica. Suggerisco di leggere l'articolo di Russ Cox a cui rimando.
Denys Séguret,

1
Questo mi ha solo aiutato a dare un senso ai suggerimenti in un modo che non sono mai stato in grado di fare in C o C ++ ... grazie mille per questa spiegazione elegante e semplice :-)
mindplay.dk

2
Bene, ancora non capisco perché *SomeInterfacenon è semplicemente un errore di compilazione?
sazary,

2
@charneykaye Non hai completamente ragione qui. Non hai mai * SomeInterface quando dichiari una variabile di interfaccia o quando restituisci un tipo di interfaccia come parte di una dichiarazione di funzione . Tuttavia è possibile avere * SomeInterface nei parametri di una funzione .
Arauter,

7

Questo è forse quello che volevi dire:

package main

type Interface interface{}

type Struct struct{}

func main() {
        var ps *Struct
        var pi *Interface
        pi = new(Interface)
        *pi = ps

        _, _ = pi, ps
}

Compila OK. Vedi anche qui .


Questo dovrebbe essere accettato, l'altro non risponde davvero alla domanda.
DrKey,


0

Sto usando il seguente modo interface{}mentre sto solo consumando eventsI interface{}come argomenti, sono ancora in grado di inviare un puntatore Struct come puoi vedere di seguito.

func Wait(seconds float64) *WaitEvent {
    return WaitEventCreate(seconds)
}

main.go

var introScene = []interface{}{
        storyboard.Wait(5),
        storyboard.Wait(2),
    }

    var storyboardI = storyboard.Create(stack, introScene)
    stack.Push(&storyboardI)

Ora all'interno della storyboard.gofunzione di creazione del file

type Storyboard struct {
    Stack  *gui.StateStack
    Events []interface{} //always keep as last args
}

func Create(stack *gui.StateStack, eventsI interface{}) Storyboard {
    sb := Storyboard{
        Stack: stack,
    }

    if eventsI != nil {
        events := reflect.ValueOf(eventsI)
        if events.Len() > 0 {
            sb.Events = make([]interface{}, events.Len())
            for i := 0; i < events.Len(); i++ {
                sb.Events[i] = events.Index(i).Interface()
            }
        }
    }

    return sb
}

Come puoi vedere sopra, Storyboard.go sta consumando solo Events []interface{}ma in realtà sto inviando è un puntatore Struct e funziona bene.

un altro esempio in più qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.