1

What's a data structure that only allows one of a possible set of options?

I tried playing around with enums but they are not what I want.

package main

import "fmt"

type Event struct {
        day_number Day 
}

type Day int 

const (
        Monday Day = iota
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
        Sunday
)

func main() {
        var r Event
        r.day_number = Monday
        fmt.Println(r.day_number)
        // Keep this from happening.
        var impossible Event
        impossible.day_number = 12
        fmt.Println(impossible.day_number)
}
Alexander Kleinhans
  • 5,950
  • 10
  • 55
  • 111

2 Answers2

2

You could hide away the member field using a different package. This limits ways of creating the structure to functions from that package, and you can then control those functions to accept a limited set of inputs.

foo/foo.go:

package foo

import "fmt"

type entity int

const (
    one entity = iota + 1
    two
)

type Foo struct {
    e entity
}

func (f Foo) Get() int {
    return int(f.e)
}

func NewFoo(i int) Foo {
    switch i {
    case 1:
        return Foo{one}
    case 2:
        return Foo{two}
    default:
        panic(fmt.Errorf("%s", "foo"))
    }
}

bar.go:

package main

import "fmt"
import "./foo"

func main() {
    f := foo.NewFoo(2)
    fmt.Println(f.Get())
    e := foo.Foo{3}  // Error: implicit assignment of unexported field 'e' in foo.Foo literal
    fmt.Println(e.Get())
    d := foo.NewFoo(3) // panic: foo!
    fmt.Println(d.Get())
}

You cannot create a Foo struct outside of the foo package, and the only function that creates Foo structs in a foo package accepts only a limited set of values.

muru
  • 4,723
  • 1
  • 34
  • 78
0

I'm not sure this makes much sense since your day_number field is already inaccessible outside your package.

Still, if this is really necessary, you'd set the private field via a method that validates the input such as the SetDay method below:

func (d Day) String() string {
    if v, ok := map[Day]string{
        Monday:    "Monday",
        Tuesday:   "Tuesday",
        Wednesday: "Wednesday",
        Thursday:  "Thursday",
        Friday:    "Friday",
        Saturday:  "Saturday",
        Sunday:    "Sunday",
    }[d]; ok {
        return v
    }
    return "Bad day"
}

func (e *Event) SetDay(d Day) error {
    if v := d.String(); v == "Bad day" {
        return fmt.Errorf(v)
    }
    e.day_number = d
    return nil
}

Playground link