By Jon L.


2012-10-21 03:26:10 8 Comments

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?

6 comments

@Luke Maurer 2019-03-26 22:35:28

Even if Go's implementation allowed this, it's unfortunately unsound: You can't assign a []Person to a variable of type []Model because a []Model has different capabilities. For example, suppose we also have Animal which implements Model:

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

If we allow line 2, then line 3 should also work because models can perfectly well store an Animal. And line 4 should still work because people stores Persons. But then we end up with a variable of type Person holding an Animal!

Java actually allows the equivalent of line 2, and it's widely considered a mistake. (The error is caught at run time; line 3 would throw an ArrayStoreException.)

@slavikm 2015-01-28 05:00:52

As others have already answered, []T is a distinct type. I'd just like to add that a simple utility can be used to convert them generically.

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's not slice or array
    intf := make([]interface{}, v.Len())
    for i := 0; i < v.Len(); i++ {
        intf[i] = v.Index(i).Interface()
    }
    return intf
}

Now, you can use it like this:

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

@Stephen Weinberg 2012-10-21 03:59:13

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

@dskinner 2012-10-21 04:12:02

Didn't realize at the time, but my answer doesn't directly address the issue raised. This answer addresses it.

@I82Much 2012-10-21 17:16:41

Very nice answer, especially with the explanation about the number of words of memory for each type

@Dalibor Filus 2016-08-04 14:24:10

@user2312578 2017-04-26 14:58:09

Well how do you write something in an abstract, generic way then? Imagine you have 100k results from a database query and you have to iterate over them all and create a copy of each dataset. This means you can't have generic interfaces in Go

@Noam Nelke 2019-03-04 10:08:18

Your answer is correct, but the explanation made me think that if I had an interface A which implements my desired interface B I would be able to use a slice of As as a slice of Bs (after all, the memory structure is identical, since they're both interfaces). I was disappointed to discover I can't :(

@Stephen Weinberg 2019-03-06 02:16:10

Interface A and interface B have different vtables. So it is not quite the same layout in memory. If it treated interface A the same way it treated interface B, it might try to execute the wrong function. You can use unsafe.Pointer to try this out yourself. It would be an interesting experiment!

@nemo 2012-10-21 04:12:19

As Stephen already answered the question and you're a beginner I emphasize on giving advises.

A better way of working with go's interfaces is not to have a constructor returning the interface as you might be used to from other languages, like java, but to have a constructor for each object independently, as they implement the interface implicitly.

Instead of

newModel(type string) Model { ... }

you should do

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

with Person and Politician both implementing the methods of Model. You can still use Person or Politician everywhere where a Model is accepted, but you can also implement other interfaces.

With your method you would be limited to Model until you do a manual conversion to another interface type.

Suppose I have a Person which implements the method Walk() and a Model implements ShowOff(), the following would not work straight forward:

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

However this would:

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

@Jon L. 2012-10-21 04:45:18

You're correct :-( My approach is my attempt to generically provide access to models via a ReST API. So a request to /api/{collection} would dynamically interact w/ the requested collection. Can you suggest an alternative solution without explicitly testing the requested collection across multiple functions? I guess what I'm looking for is a way to specify a generic return type without losing knowledge of it's type.

@nemo 2012-10-21 14:13:47

Knowledge of the type is always preserved, even if you return interface{}, the problem is that you're then need to do runtime type assertions instead of compiler type checking. A generic solution for name -> object without runtime assertions is only possible using generics, which go does not support. So if you do this, you have to live with reflection or the drawbacks of your solution.

@user2312578 2017-04-26 15:05:43

point is []Model != []Person or even []Model != []Politician. That's the issue.

@nemo 2017-04-26 17:07:42

@dalu Correct. I mention this at the top of my answer as well: this answer gives complementary information regarding typing and go interface conventions that the OP did not seem to know about but the accepted answer did not address.

@dskinner 2012-10-21 04:05:35

Maybe this is an issue with your return type *[]Person, where it should actually be []*Person so to reference that each index of the slice is a reference to a Person, and where a slice [] is in itself a reference to an array.

Check out the following example:

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())
}

@Jon L. 2012-10-21 04:27:57

Thanks for the response. While (as you noted) it doesn't directly address the issue at hand, it's an appreciated example nonetheless :-)

@zzzz 2012-10-21 03:58:56

Types T and []T are distinct types and distinct are their methods as well, even when satisfying the same interface. IOW, every type satisfying Model must implement all of the Model's methods by itself - the method receiver can be only one specific type.

Related Questions

Sponsored Content

6 Answered Questions

[SOLVED] Concatenate two slices in Go

0 Answered Questions

Assign additional field when unmarshalling JSON object to GO struct

  • 2018-09-22 21:13:55
  • Hennessy
  • 94 View
  • 0 Score
  • 0 Answer
  • Tags:   json go struct

2 Answered Questions

[SOLVED] Go function types that return structs being used with interfaces

  • 2017-02-17 17:07:10
  • Iain Duncan
  • 527 View
  • 2 Score
  • 2 Answer
  • Tags:   function go

1 Answered Questions

1 Answered Questions

[SOLVED] How to return dynamic type struct in Golang?

  • 2016-02-26 16:51:09
  • pregmatch
  • 20143 View
  • 17 Score
  • 1 Answer
  • Tags:   go revel

1 Answered Questions

[SOLVED] Nil slice vs empty slice and nil value comparison

  • 2015-12-16 12:25:12
  • nilbot
  • 1089 View
  • 1 Score
  • 1 Answer
  • Tags:   go

2 Answered Questions

[SOLVED] In go, how do you create an interface when methods are called by *Type?

  • 2011-06-22 06:04:19
  • cc young
  • 2610 View
  • 5 Score
  • 2 Answer
  • Tags:   types go

1 Answered Questions

[SOLVED] Get struct value from interface

  • 2015-04-18 03:21:54
  • RockyMountainHigh
  • 1926 View
  • 1 Score
  • 1 Answer
  • Tags:   go

2 Answered Questions

[SOLVED] Iterate Over String Fields in Struct

  • 2014-01-21 00:02:48
  • elithrar
  • 8508 View
  • 7 Score
  • 2 Answer
  • Tags:   go

4 Answered Questions

[SOLVED] Can't return nil, but zero value of slice

  • 2012-12-20 09:58:26
  • Sergi Mansilla
  • 2352 View
  • 2 Score
  • 4 Answer
  • Tags:   go

Sponsored Content