3

I want to create a function, which uses a slice of an interface.

For example I have an interface ider which wraps one ID() string function. Then I have a type (Field), which implements the ider interface. Now I have different objects, which have slice of ider as parameter.

Inside my function I expect a slice of ider ([]ider). That function should be used by different types, which are implementing the ider.

This is hard to describe. So here is a complete example, which outputs the following error:

cannot use myObj.Fields (type []*Field) as type []ider in argument to inSlice

type ider interface {
    ID() string
}

type Field struct {
    Name string
}

// Implements ider interface
func (f *Field) ID() string {
    return f.Name
}

type MyObject struct {
    Fields []*Field
}

// uses slice of ider
func inSlice(idSlice []ider, s string) bool {
    for _, v := range idSlice {
        if s == v.ID() {
            return true
        }
    }
    return false
}

func main() {
    fields := []*Field{
        &Field{"Field1"},
    }
    myObj := MyObject{
        Fields: fields,
    }
    fmt.Println(inSlice(myObj.Fields, "Field1"))
}

https://play.golang.org/p/p8PPH51vxP

I already searched for answers, but I did just found solutions for empty interfaces and not for specific ones.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
apxp
  • 5,240
  • 4
  • 23
  • 43
  • May be use the idler interface to create the "fields" variable, like this: fields := []ider{ &Field{"Field1"},}. It runs on your go playground link. – mgagnon Apr 12 '17 at 10:30
  • 5
    Possible duplicate of [golang: slice of struct != slice of interface it implements?](http://stackoverflow.com/questions/12994679/golang-slice-of-struct-slice-of-interface-it-implements) –  Apr 12 '17 at 10:41
  • @mgagnon yes that works. I just edited the question, because my problem is nested inside an additional type. – apxp Apr 12 '17 at 11:00
  • 1
    From the Tim Cooper link, there a reference to that: https://github.com/golang/go/wiki/InterfaceSlice I guess it will help you. – mgagnon Apr 12 '17 at 11:49
  • Thanks. That link helped. – apxp Apr 12 '17 at 11:56

1 Answers1

3

As one can read in https://golang.org/ref/spec#Calls

Except for one special case, arguments must be single-valued expressions assignable to the parameter types of F and are evaluated before the function is called.

So in the above code myObj.Fields is of type []*Field which needs to be assignable to []ider for the code to compile. Let's check wether that is the case. As one can read in https://golang.org/ref/spec#Assignability

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

  • x's type is identical to T.
  • x's type V and T have identical underlying types and at least one of V or T is not a named type.
  • T is an interface type and x implements T.
  • x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
  • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
  • x is an untyped constant representable by a value of type T.
  1. Are []*Field and []ider identical? https://golang.org/ref/spec#Type_identity tells us

    Two slice types are identical if they have identical element types.

    So are *Field and ider identical? The above source tells us

    A named and an unnamed type are always different.

    So no and no, as *Field is unnamed and ider is named.

  2. The underlying type of []*Field is []*Field and the underlying type of []ider is []ider, and those are not identical, as we checked in 1. so this is also not applicable. Read here https://golang.org/ref/spec#Types

  3. is not applicable as []ider is not an interface type, but a slice type. Read here https://golang.org/ref/spec#Slice_types

  4. also not applicable as there is no channel used

  5. also not applicable as there is no nil used

  6. also not applicable as there is no constant used.

So summing up: A value of type []*Field is not assignable to []ider and therefore we can't use an expression of type []*Field in a parameter position of a function call with parameter type []ider.

typetetris
  • 4,586
  • 16
  • 31
  • In go you would usually do something like `var asIder []ider; for _, v := range myObj.Fields { asIder = append(asIder, v) }; inSlice(asIder, "Field1")` or wrap the conversion up in a function. – typetetris Apr 12 '17 at 15:45