Подтвердить что ты не робот

Golang: кусочек структуры! = Кусочек интерфейса, который он реализует?

У меня есть интерфейс Model, который реализуется struct Person.

Чтобы получить экземпляр модели, у меня есть следующие вспомогательные функции:

func newModel(c string) Model {
    switch c {
    case "person":
        return newPerson()
    }
    return nil
}

func newPerson() *Person {
    return &Person{}
}

Приведенный выше подход позволяет мне возвращать правильно типизированный экземпляр Person (можно легко добавить новые модели позже с таким же подходом).

Когда я попытался сделать что-то подобное для возврата куска моделей, я получаю сообщение об ошибке. Код:

func newModels(c string) []Model {
    switch c {
    case "person":
        return newPersons()
    }
    return nil
}

func newPersons() *[]Person {
    var models []Person
    return &models
}

Go жалуется на: cannot use newPersons() (type []Person) as type []Model in return argument

Моя цель - вернуть фрагмент любого типа модели ([]Person, []FutureModel, []Terminator2000, w/e). Что мне не хватает, и как я могу правильно реализовать такое решение?

4b9b3361

Ответ 1

Это очень похоже на вопрос, на который я только что ответил: fooobar.com/questions/97454/...

Короткий ответ: вы правы. Кусочек структур не равен фрагменту интерфейса, реализуемого структурой.

A []Person и a []Model имеют разные макеты памяти. Это связано с тем, что типы, на которых они расположены, имеют разные макеты памяти. A Model - значение интерфейса, что означает, что в памяти это два слова по размеру. Одно слово для информации о типе, другое для данных. A Person - это структура, размер которой зависит от содержащихся в ней полей. Чтобы преобразовать из []Person в []Model, вам нужно будет перебрать массив и выполнить преобразование типа для каждого элемента.

Поскольку это преобразование является операцией O (n) и приведет к созданию нового среза, Go отказывается делать это неявно. Вы можете сделать это явно со следующим кодом.

models := make([]Model, len(persons))
for i, v := range persons {
    models[i] = Model(v)
}
return models

И как dscinner указал, вам, скорее всего, нужен фрагмент указателей, а не указатель на фрагмент. Указатель на срез обычно не требуется.

*[]Person        // pointer to slice
[]*Person        // slice of pointers

Ответ 2

Возможно, это проблема с вашим типом возвращаемого значения *[]Person, где оно должно быть []*Person, поэтому ссылаться на то, что каждый индекс среза является ссылкой на Person, и где срез [] равен сама по себе ссылка на массив.

Посмотрите следующий пример:

package main

import (
    "fmt"
)

type Model interface {
    Name() string
}

type Person struct {}

func (p *Person) Name() string {
    return "Me"
}

func NewPersons() (models []*Person) {
    return models
}

func main() {
    var p Model
    p = new(Person)
    fmt.Println(p.Name())

    arr := NewPersons()
    arr = append(arr, new(Person))
    fmt.Println(arr[0].Name())
}

Ответ 3

Как Стивен уже ответил на вопрос, и вы новичок, я подчеркиваю, что даю советы.

Лучшим способом работы с интерфейсами go не является возврат конструктора интерфейс, как вы могли бы использовать с других языков, таких как java, но иметь конструктор для каждого объекта независимо, поскольку они реализуют интерфейс неявно.

Вместо

newModel(type string) Model { ... }

вы должны сделать

newPerson() *Person { ... }
newPolitician() *Politician { ... }

с Person и Politician, реализуя методы Model. Вы все равно можете использовать Person или Politician везде, где a Model , но вы также можете реализовать другие интерфейсы.

С помощью вашего метода вы будете ограничены Model, пока не выполните ручное преобразование в другой тип интерфейса.

Предположим, что у меня есть Person, который реализует метод Walk() и Model реализует ShowOff(), следующее не будет работать прямо:

newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk

Однако это:

newPerson().ShowOff()
newPerson().Walk()

Ответ 4

Типы T и [] T - это разные типы и различные их методы, даже если они удовлетворяют одному и тому же интерфейсу. IOW, каждый тип, удовлетворяющий модели, должен реализовывать все методы модели самостоятельно - приемник метода может быть только одним конкретным типом.

Ответ 5

Как уже ответили другие, [] T - отличный тип. Я хотел бы добавить, что для их преобразования можно использовать простую утилиту.

import "reflect"

// Convert a slice or array of a specific type to array of interface{}
func ToIntf(s interface{}) []interface{} {
    v := reflect.ValueOf(s)
    // There is no need to check, we want to panic if it not slice or array
    intf := make([]interface{}, v.Len())
    for i := 0; i < v.Len(); i++ {
        intf[i] = v.Index(i).Interface()
    }
    return intf
}

Теперь вы можете использовать его следующим образом:

ToIntf([]int{1,2,3})

Ответ 6

Даже если реализация Go позволила это, это, к сожалению, несостоятельно: вы не можете назначить []Person переменной типа []Model потому что у []Model []Model есть разные возможности. Например, предположим, что у нас также есть Animal который реализует Model:

var people []Person = ...
var models []Model = people // not allowed in real Go
models[0] = Animal{..} // ???
var person Person = people[0] // !!!

Если мы разрешаем строку 2, то строка 3 также должна работать, потому что models могут отлично хранить Animal. И строка 4 все еще должна работать, потому что people хранят Person s. Но затем мы получаем переменную типа Person содержащую Animal !

Java фактически допускает эквивалент строки 2, и это широко считается ошибкой. (Ошибка перехватывается во время выполнения; строка 3 ArrayStoreException.)