Почему я не могу назначить * Struct * интерфейсу?


142

Я просто прорабатываю тур по Go и не понимаю, как насчет указателей и интерфейсов. Почему этот код Go не компилируется?

package main

type Interface interface {}

type Struct struct {}

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

    _, _ = pi, ps
}

т.е. если Structесть Interface, почему бы не *Structбыть *Interface?

Я получаю следующее сообщение об ошибке:

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


это выглядит как интерфейс может ведет себя как неявные указатели ...
Виктор

могу я предложить обогатить вашу игровую площадку 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 }и сделать собственные выводы
Виктор

Ответы:


188

Когда у вас есть структура, реализующая интерфейс, указатель на эту структуру также автоматически реализует этот интерфейс. Вот почему у вас никогда не было *SomeInterfaceв прототипе функций, так как это ничего бы не добавило SomeInterface, и вам не нужен такой тип в объявлении переменной (см. Этот связанный вопрос ).

Значение интерфейса - это не значение конкретной структуры (поскольку у него переменный размер, это было бы невозможно), а своего рода указатель (точнее, указатель на структуру и указатель на тип ). Расс Кокс описывает это именно здесь :

Значения интерфейса представлены как пара из двух слов, дающая указатель на информацию о типе, хранящемся в интерфейсе, и указатель на связанные данные.

введите описание изображения здесь

Вот почему Interface, а не *Interfaceправильный тип для хранения указателя на реализацию структуры Interface.

Поэтому вы должны просто использовать

var pi Interface

8
Хорошо, я думаю, что это имеет для меня смысл. Мне просто интересно, почему (в таком случае) это не просто ошибка времени компиляции var pi *Interface.
Саймон Никерсон

1
Я подробно объяснил это. См. Редактировать. Предлагаю прочитать статью Расса Кокса, на которую я ссылаюсь.
Denys Séguret,

1
Это просто помогло мне разобраться в указателях так, как я никогда не мог сделать на C или C ++ ... Большое спасибо за это элегантное и простое объяснение :-)
mindplay.dk

2
Хорошо, я все еще не понимаю, почему *SomeInterfaceэто не просто ошибка компиляции?
sazary

2
@charneykaye Вы здесь не совсем правы. У вас никогда не будет * SomeInterface при объявлении переменной интерфейса или при возврате типа интерфейса как части объявления функции . Однако вы можете иметь * SomeInterface в параметрах функции .
arauter 06

8

Возможно, вы имели в виду:

package main

type Interface interface{}

type Struct struct{}

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

        _, _ = pi, ps
}

Компилируется нормально. Смотрите также здесь .


Это следует принять, другой на самом деле не отвечает на вопрос.
DrKey


0

Я использую следующий способ, interface{}пока я просто использую в eventsI interface{}качестве аргументов, я все еще могу отправлять указатели структуры, как вы можете видеть ниже.

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)

Теперь внутри storyboard.goфайла Create функция

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
}

Как вы можете видеть выше, Storyboard.go просто потребляет, Events []interface{}но на самом деле я отправляю указатель Struct, и он отлично работает.

еще один пример здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.