在Go语言中,可以使用集合中只允许一个元素的数据结构。

huangapple go评论82阅读模式
英文:

Data structure that only allows one of a set? Golang

问题

只允许一组可能选项的数据结构是什么?

我尝试使用enum进行操作,但它们不是我想要的。

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)
    // 防止这种情况发生。
    var impossible Event
    impossible.day_number = 12
    fmt.Println(impossible.day_number)
}
英文:

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)
}

答案1

得分: 2

你可以使用不同的包来隐藏成员字段。这将限制只能使用该包中的函数来创建该结构,并且你可以控制这些函数只接受有限的输入集合。

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}  // 错误:在 foo.Foo 字面值中隐式分配了未导出的字段 'e'
    fmt.Println(e.Get())
    d := foo.NewFoo(3) // panic: foo!
    fmt.Println(d.Get())
}

foo 包之外无法创建 Foo 结构体,而在 foo 包中,只有一个函数可以创建 Foo 结构体,并且只接受有限的值。

英文:

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.

答案2

得分: 0

我不确定这是否有太多意义,因为你的day_number字段已经无法在包外访问。

不过,如果这真的是必要的,你可以通过一个验证输入的方法来设置私有字段,例如下面的SetDay方法:

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链接

英文:

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

huangapple
  • 本文由 发表于 2016年12月22日 14:18:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/41276859.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定