John Difool
John Difool

Reputation: 5732

How can I use interface{} as a wildcard type?

The scenario is to pass similar structs with common fields and set those to values passed as params:

package main

type A struct {
    Status int
}

type B struct {
    id     string
    Status int
}

// It's okay to pass by value because content is only used inside this
func foo(v interface{}, status int) {
    switch t := v.(type) {
    case A, B:
        t.Status = status // ERROR :-(
    }
}

func main() {
    a := A{}
    foo(a, 0)
    b := B{}
    b.id = "x"
    foo(b, 1)
}

To my dismay, I am getting this error:

➜  test  go run test.go
# command-line-arguments
./test.go:15: t.Status undefined (type interface {} has no field or method Status)

What am I doing wrong if the typecast converts interface{} to the underlying type?

Upvotes: 1

Views: 1129

Answers (1)

captncraig
captncraig

Reputation: 23138

Even though A and B both have a status field they are not interchangeable to the type system. You must have separate cases for each of them.

case A:
    t.Status = status
case B:
    t.Status = status
} 

playground link

Alternatively, you could use an actual interface:

type HasStatus interface {
  SetStatus(int)
}

type A struct {
   Status int
}

func (a *A) SetStatus(s int) { a.Status = s }

func foo(v HasStatus, status int) {
    v.SetStatus(status)
}

full example

If you have multiple types that all have a common set of fields you may want to use an embedded struct:

type HasStatus interface {
    SetStatus(int)
}

type StatusHolder struct {
    Status int
}

func (sh *StatusHolder) SetStatus(s int) { sh.Status = s }

type A struct {
    StatusHolder
}

type B struct {
    id string
    StatusHolder
}

full example

Upvotes: 5

Related Questions