lf215
lf215

Reputation: 1009

golang 2 slice types with 1 field

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.

Upvotes: 3

Views: 557

Answers (2)

peterSO
peterSO

Reputation: 166569

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
}

Upvotes: 3

twinj
twinj

Reputation: 2039

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

Upvotes: 3

Related Questions