7

枚举类型在Go语言中的一种写法

 3 years ago
source link: https://www.zenlife.tk/go-enum.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

枚举类型在Go语言中的一种写法

2017-03-06

枚举类型在ML类语言里面写起来是最自然的,比如ocaml,F#里面:

type T = 
| T1 of int 
| T2 of string
| T3 of bool

haskell里面好像用的datetype而不是type,不太记得细节。总之这是非常重要的一种类型,并不仅限于函数式语言中。

在C语言里面,没有类型安全的保证。好在C里面有union类型,并且有指针,指针这东西太灵活了,能玩得飞起。在C里面枚举类型可以这样写:

struct T {
  int kind;
  union {
    char* T1;
    int T2;
    bool T3;
  } value;
};
这里有个小的细节,union分配的大小等于结构体里面最大的那个,所以这种写法不一定最优。C足够灵活,就不多说了。

在Go里面,连union关键字都没有了,类型也做的更严格,指针不像C那样可以随意转。可能比较标准的做法是用interface。interface也是可以玩得飞起的,比如这篇文章以前探索过。但是现在回头想想,觉得太复杂。

interface{}像C的指针一样,它可以是任何类型。但是有些问题吧,第一个问题,它的类型安全是靠运行时判断的,相比函数式语言只产生编译期开销,还逊色不少。

另一个问题是对代码洁癖者说的,其实只要代码里出写:

if _, ok := t.(T); ok {}

我认为基本算得上bad smell了。Go语言讲究依赖于接口而不是实现(其实原则上任何语言都是),你都使用接口了,还需要反射出具体的实现来,那就是丑。

再一个重要的问题当然是性能了。参数传递会有对象分配,用interface反射出原始值也会有对象分配。即无论你是

func f(v interface{}) {}
f(3) // 由于参数是interface,这里会分配一个interface对象,对象的值指向3
raw, ok := t.(T)   // raw不是原始值,而是分配一个T并从原始值拷贝。

都是有内存分配开销的。在一些性能重要的场合,这种开销对GC还是危害挺大的。

反正觉得在Go里面用interface实现枚举类型不算是优雅的方案。

下面就是我觉得更好的一种写法了。

先定义嵌入类:

type T struct {
    kind enumT
}
在实现要枚举的T1 T2 T3,都嵌入这个类型:
type T1 struct {
    T
    v int
}
type T2 struct {
    T
    v string
}
type T3 struct {
    T
    v bool
}

实际使用的时候,我们的类型都是*T,判断kind去获知真实类型:

var t *T
switch t.kind { case kindT1: case kindT2: default: }

创建牧举类型的对象的写法:

var t *T
tmp := T1{}
tmp.kind = kindT1
t = &tmp.T

当然最好是封装成函数

func createT1() *T
func createT2() *T
func createT3() *T

可以为kind的类型做一个别名,并定义常量:

type enumT int
const (
    kindT1 enumT = iota
    kindT2
    kindT3
)

用枚举 *T 反射出真实的类型 *T1 需要用到指针转换,用unsafe,其实也可以用函数封起来:

var t T
raw := (*T1)(unsafe.Pointer(t))

相比于用interface,这种写法是跟C的指针一样无开销,并且对于GC也是安全的(用unsafe.Pointer存东西是不安全的)。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK