我正在尝试在golang中使用protobuf2枚举,但我无法弄清楚。

我创建了一个简单的protobuf文件:

1
2
3
4
5
6
7
8
syntax         =         "proto2" ;
package                    enum    ;
message Foo{
     enum Bar{
         LOL = 1;
     }
     optional Bar baz = 1;
}

然后我创建了一个简单的golang文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
    enum"./enum"
   "github.com/golang/protobuf/proto"
)

func main() {
    msg := &enum.Foo{
        Baz: enum.Foo_LOL,
    }
    proto.Marshal(&msg)
}

我遇到错误。

1
./foo.go:10: cannot use enum.Foo_LOL (type enum.Foo_Bar) as type *enum.Foo_Bar in field value

解决起来似乎很简单,只需在enum.Foo_Bar前面添加一个&

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
    enum"./enum"
   "github.com/golang/protobuf/proto"
)

func main() {
    msg := &enum.Foo{
        Baz: &enum.Foo_LOL,
    }
    proto.Marshal(&msg)
}

否:

1
./foo.go:10: cannot take the address of enum.Foo_LOL

我搜索了google,发现这个家伙正在被漫游器诱骗。他有一些有效的代码,但是它很冗长,足以让一位圣经学者感到厌倦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
    enum"./enum"
   "github.com/golang/protobuf/proto"
)

var lolVar = enum.Foo_LOL

func main() {
    msg := &enum.Foo{
        Baz: &lolVar,
    }
    proto.Marshal(msg)
}

我查看了生成的代码,发现了一个Enum方法,该方法也可以工作,但是很冗长,足以招募税务审计师:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
    enum"./enum"
   "github.com/golang/protobuf/proto"
)

func main() {
    msg := &enum.Foo{
        Baz: enum.Foo_LOL.Enum(),
    }
    proto.Marshal(msg)
}

预期的方法是什么?


具有syntax="proto2"

Protobuf生成具有指针类型的枚举字段,因此Foo.baz的类型为*Foo_Bar。您不能将非指针Foo_Bar值分配给该字段,只能分配一个指针值。

此外,您在protobuf文件中列出的枚举值将在Go中生成为常量。而且您不能获取常量值的地址,有关详细信息,请参见:在go

中查找常量的地址

如果您的protobuf生成了一个Enum()方法,该方法返回了指向该值的指针,那么可以使用它。但是此方法并不总是生成的,因此,如果找不到某种类型的枚举/枚举,请不要感到惊讶。

如果丢失,最简单的方法是创建一个(本地)变量,您可以使用其地址:

1
2
3
4
5
lol := enum.Foo_LOL
msg := &enum.Foo{
    Baz: &lol,
}
err := proto.Marshal(msg)

如果必须多次/多次执行此操作,请创建一个辅助函数,该函数可以满足Enum()方法的目的。这是创建一个的方法:

1
func barPtr(b enum.Foo_Bar) *enum.Foo_Bar { return &b }

并使用它:

1
2
3
4
msg := &enum.Foo{
    Baz: barPtr(enum.Foo_LOL),
}
err := proto.Marshal(msg)

还有许多其他选项可以获取指向整数类型的指针,但是它们不一定更干净或更有效。您可以在此处看到不同方法的列表:如何在Go中执行文字* int64?

  • 什么时候以及为什么不创建Enum方法?
  • @timthelion我不确定,但是我也使用protobuf,并且没有为我的枚举生成此Enum()方法。
  • 您是否有机会使用proto3?查看代码,我发现不是为proto3文件创建的Enum方法,可能是因为proto3不支持可选字段。
  • @timthelion是的,刚要发表评论。我已经调查过了。如果使用syntax="proto2",则将字段作为指针生成,并且将Enum()方法添加到枚举中。当为syntax="proto3"时,该字段为非指针,并且不会生成任何Enum()方法。因此,如果您"升级"到"proto3",则将"丢失" Enum()方法。是的,在proto3中,默认情况下字段为optional