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). Что мне не хватает, и как я могу правильно реализовать такое решение?
Ответы
Ответ 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
.)