Por que não consigo atribuir uma * Struct a uma * Interface?

142

Estou apenas trabalhando no tour Go e estou confuso sobre ponteiros e interfaces. Por que esse código Go não é compilado?

package main

type Interface interface {}

type Struct struct {}

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

    _, _ = pi, ps
}

ou seja, se Structé um Interface, por que não *Structseria um *Interface?

A mensagem de erro que recebo é:

prog.go:10: cannot use ps (type *Struct) as type *Interface in assignment:
        *Interface is pointer to interface, not interface
Simon Nickerson
fonte
parece que as interfaces poderia se comporta como os ponteiros implícitos ...
Victor
Posso sugerir para enriquecer o seu playground com 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 fazer suas próprias conclussions
Victor

Respostas:

183

Quando você tem uma estrutura implementando uma interface, um ponteiro para essa estrutura também implementa automaticamente essa interface. É por isso que você nunca possui *SomeInterfaceo protótipo de funções, pois isso não adicionaria nada a isso SomeInterfacee você não precisa desse tipo de declaração de variável (consulte esta pergunta relacionada ).

Um valor de interface não é o valor da estrutura concreta (como tem um tamanho variável, isso não seria possível), mas é um tipo de ponteiro (para ser mais preciso, um ponteiro para a estrutura e um ponteiro para o tipo ) Russ Cox descreve exatamente aqui :

Os valores da interface são representados como um par de duas palavras, fornecendo um ponteiro para informações sobre o tipo armazenado na interface e um ponteiro para os dados associados.

insira a descrição da imagem aqui

É por isso que Interface, e não, *Interfaceé o tipo correto para armazenar um ponteiro para uma implementação de estrutura Interface.

Então você deve simplesmente usar

var pi Interface
Denys Séguret
fonte
8
OK, acho que isso faz sentido para mim. Estou apenas me perguntando por que (nesse caso), não é simplesmente um erro de tempo de compilação a dizer var pi *Interface.
Simon Nickerson
1
Entrei em mais detalhes para explicar isso. Veja editar. Sugiro a leitura do artigo de Russ Cox ao qual vinculo.
Denys Séguret 22/11/2012
1
Isso só me ajudou a fazer sentido dos ponteiros em uma maneira que eu era nunca mais capaz de fazer em C ou C ++ ... muito obrigado por esta explicação elegante e simples :-)
mindplay.dk
2
Tudo bem, eu ainda não entendo porque *SomeInterfacenão é simplesmente um erro de compilação?
sazary
2
@charneykaye Você não está totalmente correto aqui. Você nunca possui * SomeInterface ao declarar uma variável de interface ou ao retornar um tipo de interface como parte de uma declaração de função . No entanto, você pode ter * SomeInterface dentro dos parâmetros de uma função .
Arauter 6/12/17
7

Talvez seja isso que você quis dizer:

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. Veja também aqui .

zzzz
fonte
Isso deve ser aceito, o outro realmente não responde à pergunta.
DrKey
0

Aqui está uma maneira muito simples de atribuir uma estrutura a uma interface:

package main

type Interface interface{}

type Struct struct{}

func main() {
    ps := new(Struct)
    pi := Interface(ps)

    _, _ = pi, ps
}

https://play.golang.org/p/BRTaTA5AG0S

Miguel Mota
fonte
0

Estou usando a seguinte maneira de interface{}consumir apenas eventsI interface{}argumentos, mas ainda posso enviar um ponteiro de estrutura como você pode ver abaixo.

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)

Agora dentro da storyboard.gofunção Criar arquivo

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 você pode ver acima, o Storyboard.go está consumindo apenas, Events []interface{}mas, na verdade, estou enviando um ponteiro Struct e funciona bem.

outro exemplo mais aqui

AÇO
fonte