¿Por qué no puedo asignar una estructura * a una interfaz *?


142

Solo estoy trabajando en la gira Go , y estoy confundido acerca de los punteros y las interfaces. ¿Por qué no compila este código Go?

package main

type Interface interface {}

type Struct struct {}

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

    _, _ = pi, ps
}

es decir, si Structes un Interface, ¿por qué no *Structsería un *Interface?

El mensaje de error que recibo es:

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


parece que las interfaces podrían comportarse como punteros implícitos ...
Victor

¿Puedo sugerirle enriquecer su patio de recreo 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 }y hacer sus propias conclusiones
Victor

Respuestas:


183

Cuando tiene una estructura que implementa una interfaz, un puntero a esa estructura implementa automáticamente esa interfaz también. Es por eso que nunca tiene *SomeInterfaceen el prototipo de funciones, ya que esto no agregaría nada SomeInterface, y no necesita ese tipo de declaración de variable (vea esta pregunta relacionada ).

Un valor de interfaz no es el valor de la estructura concreta (ya que tiene un tamaño variable, esto no sería posible), pero es una especie de puntero (para ser más precisos, un puntero a la estructura y un puntero al tipo ) Russ Cox lo describe exactamente aquí :

Los valores de la interfaz se representan como un par de dos palabras que proporciona un puntero a la información sobre el tipo almacenado en la interfaz y un puntero a los datos asociados.

ingrese la descripción de la imagen aquí

Esta es la razón Interface, y no *Interfacees el tipo correcto para mantener un puntero a una implementación de estructura Interface.

Entonces simplemente debes usar

var pi Interface

8
OK, creo que eso tiene sentido para mí. Me pregunto por qué (en ese caso), no es simplemente un error de tiempo de compilación var pi *Interface.
Simon Nickerson el

1
Entré en más detalles para explicarlo. Ver editar. Sugiero la lectura del artículo de Russ Cox con el que enlazo.
Denys Séguret

1
Esto me ayudó a dar sentido a los punteros de una manera que nunca pude hacer en C o C ++ ... muchas gracias por esta elegante y simple explicación :-)
mindplay.dk

2
Muy bien, todavía no entiendo ¿por qué *SomeInterfaceno es simplemente un error de compilación?
Sazary

2
@charneykaye No tienes toda la razón aquí. Nunca tiene * SomeInterface al declarar una variable de interfaz o al devolver un tipo de interfaz como parte de una declaración de función . Sin embargo , puede tener * SomeInterface dentro de los parámetros de una función .
arauter

7

Esto es quizás lo que quisiste decir:

package main

type Interface interface{}

type Struct struct{}

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

        _, _ = pi, ps
}

Compila bien. Ver también aquí .


Esto debe ser aceptado, el otro realmente no responde la pregunta.
DrKey


0

Estoy usando la siguiente forma de, interface{}aunque solo estoy consumiendo eventsI interface{}como argumentos, todavía puedo enviar un Struct Pointers como se puede ver a continuación.

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)

Ahora dentro de storyboard.gola función Crear archivo

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
}

Como puede ver arriba, el Storyboard.go está consumiendo solo, Events []interface{}pero de hecho, Im send es un puntero Struct y funciona bien.

otro ejemplo más aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.