3

In light of the fact that golang does not support unions, what is the best way to achieve:

type foo struct {
    words []string
    nums []int
}

such that only words or nums can be used at once. One thing I've tried is:

type foo struct {
    values []interface{}
}

but I prefer something that restricts the types to the two mentioned or something with pointers.

lf215
  • 1,185
  • 7
  • 41
  • 83
  • 2
    Isn't it already discussed in http://stackoverflow.com/q/21553398/6309? Not to mention http://grokbase.com/t/gg/golang-nuts/1424z09454/go-nuts-what-is-the-best-practice-for-unions-in-go – VonC Jul 27 '14 at 19:18
  • The problem is that I want the slice's values to all be the same type. I don't see to how enforce that from these links other than checking every value. – lf215 Jul 27 '14 at 20:25

2 Answers2

3

Use a foo package to hide the implementation. For example,

package foo

const (
    typeWords = iota + 1
    typeNums
)

type Foo struct {
    fooType byte
    words   []string
    nums    []int
}

func NewWords(words []string) *Foo {
    return &Foo{fooType: typeWords, words: words}
}

func NewNums(nums []int) *Foo {
    return &Foo{fooType: typeNums, nums: nums}
}

func (f *Foo) Words() []string {
    if f.fooType != typeWords {
        return nil
    }
    return f.words
}

func (f *Foo) Nums() []int {
    if f.fooType != typeNums {
        return nil
    }
    return f.nums
}

ADDENDUM:

Since we are hiding the implementation of package foo, we can implement it another way. For example, we could adopt twinj's suggestion and use an interface. To ensure some degree of generality, let's add another []string type Phrases. The value types are used to distinguish between the two []string types.

package foo

type (
    valueWords   []string
    valuePhrases []string
    valueNums    []int
)

type Foo struct {
    value interface{}
}

func NewWords(words []string) *Foo {
    return &Foo{value: valueWords(words)}
}

func (f *Foo) Words() []string {
    value, ok := f.value.(valueWords)
    if !ok {
        return nil
    }
    return value
}

func NewPhrases(phrases []string) *Foo {
    return &Foo{value: valuePhrases(phrases)}
}

func (f *Foo) Phrases() []string {
    value, ok := f.value.(valuePhrases)
    if !ok {
        return nil
    }
    return value
}

func NewNums(nums []int) *Foo {
    return &Foo{value: valueNums(nums)}
}

func (f *Foo) Nums() []int {
    value, ok := f.value.(valueNums)
    if !ok {
        return nil
    }
    return value
}
Community
  • 1
  • 1
peterSO
  • 158,998
  • 31
  • 281
  • 276
  • small note: would seem to be just as functional with "typeWords = iota" instead of "typeWords = iota + 1" – lf215 Jul 27 '14 at 21:52
  • 3
    @lf215: No it's not. I didn't want the zero value for the type to be valid. It's only valid if it's created explicitly in the `foo` package. For example, `var f foo.Foo` would have zero for the `fooType`; the `fooType` is undefined. – peterSO Jul 27 '14 at 21:57
  • Nice; I would also add a method to return the type using a type switch and also a method to check if a Foo has been initialized since value is private. – twinj Jul 28 '14 at 07:10
3

Another option would be to use an interface and type assertion.

type Foo struct {
    values interface{}
}

func (o *Foo) Words() []string {
    if v, ok := o.values.([]string); ok {
        return v 
    }
    return nil
}

func (o *Foo) Nums() []int {
     if v, ok := o.values.([]int); ok {
        return v
    }
    return nil
}

Check out the Go playground example: GoPlay

Note:

  1. Foo cannot be an interface type (type Foo []interface{} or type Foo interface{}) but can be a struct which contains an interface type.

  2. You also cannot assert []interface{} to another slice type as an []interface{} is a type in its own right. See here

Community
  • 1
  • 1
twinj
  • 2,009
  • 18
  • 10