Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

slice of struct != slice of interface it implements?

Tags:

go

I have an interface Model, which is implemented by struct Person.

To get a model instance, I have the following helper functions:

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

The above approach allows me to return a properly typed Person instance (can easily add new models later with same approach).

When I attempted to do something similar for returning a slice of models, I get an error. Code:

func newModels(c string) []Model {     switch c {     case "person":         return newPersons()     }     return nil }  func newPersons() *[]Person {     var models []Person     return &models } 

Go complains with: cannot use newPersons() (type []Person) as type []Model in return argument

My goal is to return a slice of whatever model type is requested (whether []Person, []FutureModel, []Terminator2000, w/e). What am I missing, and how can I properly implement such a solution?

like image 554
Jon L. Avatar asked Oct 21 '12 03:10

Jon L.


1 Answers

This is very similar to a question I just answered: https://stackoverflow.com/a/12990540/727643

The short answer is that you are correct. A slice of structs is not equal to a slice of an interface the struct implements.

A []Person and a []Model have different memory layouts. This is because the types they are slices of have different memory layouts. A Model is an interface value which means that in memory it is two words in size. One word for the type information, the other for the data. A Person is a struct whose size depends on the fields it contains. In order to convert from a []Person to a []Model, you will need to loop over the array and do a type conversion for each element.

Since this conversion is an O(n) operation and would result in a new slice being created, Go refuses to do it implicitly. You can do it explicitly with the following code.

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

And as dskinner pointed out, you most likely want a slice of pointers and not a pointer to a slice. A pointer to a slice is not normally needed.

*[]Person        // pointer to slice []*Person        // slice of pointers 
like image 126
Stephen Weinberg Avatar answered Oct 06 '22 19:10

Stephen Weinberg