Go:安全地将整数转换为协议缓冲区枚举值的最佳实践
Go: best practice for safely converting integers to protocol buffer enum values
我在原型文件中有一个枚举,它在 pb.go 文件中生成整数常量。我现在有一些来自外部数据源的整数,并希望将它们安全地映射到可能的常量。
这是我目前拥有的:https://play.golang.org/p/-5VZqPbukd
package main
import (
"errors"
"fmt"
)
//enum in the proto file
//
// enum X {
// A = 0;
// B = 1;
// C = 2;
// D = 3;
// }
//enum type generated by protoc
type X int32
//enum constants generated by protoc
const (
X_A X = 0
X_B X = 1
X_C X = 2
X_D X = 3
)
func intToX(v int) (X, error) {
x := X(v)
switch x {
case X_A, X_B, X_C, X_D:
return x, nil
}
return 0, errors.New("could not convert int to X")
}
func main() {
for i := -1; i < 10; i++ {
if x, err := intToX(i); err != nil {
fmt.Println("unhandled error:", err, "for input value", i)
} else {
fmt.Printf("%d => X(%d)\n", i, x)
}
}
}
问题:是否有更好、更惯用的方法将传入的整数值映射到协议生成的常量?
特别是,我想避免在 case A, B, C, D
语句中明确列出所有常量。
我不知道你用的是哪个原型生成包,但是github.com/golang/protobuf/proto
你也可以得到枚举的反向映射。
示例 xyz.pb.go 生成的文件:
type TimeInterval int32
const (
TimeInterval_TI_UNKNOWN TimeInterval = 0
TimeInterval_TI_HOUR TimeInterval = 1
TimeInterval_TI_DAY TimeInterval = 2
TimeInterval_TI_WEEK TimeInterval = 3
TimeInterval_TI_MONTH TimeInterval = 4
TimeInterval_TI_QUARTER TimeInterval = 5
TimeInterval_TI_YEAR TimeInterval = 6
)
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
var TimeInterval_value = map[string]int32{
"TI_UNKNOWN": 0,
"TI_HOUR": 1,
"TI_DAY": 2,
"TI_WEEK": 3,
"TI_MONTH": 4,
"TI_QUARTER": 5,
"TI_YEAR": 6,
}
func (x TimeInterval) String() string {
return proto.EnumName(TimeInterval_name, int32(x))
}
func (TimeInterval) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
因此,您可以通过以下方式测试是否存在:
if _, found := TimeInterval_name[testinputint]; found{
//all ok
} else {
//not a value for this enum
}
是的,@RickyA 提到使用范围很好,因为它可以验证所有可能的基础常量值。
此外,您还可以检查枚举的长度,尽管这只有在基础枚举值没有任何 'gaps' 并且具有相应的数字范围时才有可能。
代码详细解释:
typelength := int32(len(TimeInterval_name))
if testinputint < 0 || int32(testinputint) >= typelength {
// not a value for this enum, return err
}
不那么冗长,只是使用 int 而不是 int32
if testinputint < 0 || int(testinputint) >= len(TimeInterval_name) {
// not a value for this enum, return err
}
但如前所述,这仅对遵守适当 iota 的枚举有效。当您将枚举更改为读取如下内容时,情况可能并非如此:
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
// we do not use month anymore 4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
因为生成的地图的长度会明显小于六:)
也就是说使用上面@Ricky_A找到的方法以防万一
我在原型文件中有一个枚举,它在 pb.go 文件中生成整数常量。我现在有一些来自外部数据源的整数,并希望将它们安全地映射到可能的常量。
这是我目前拥有的:https://play.golang.org/p/-5VZqPbukd
package main
import (
"errors"
"fmt"
)
//enum in the proto file
//
// enum X {
// A = 0;
// B = 1;
// C = 2;
// D = 3;
// }
//enum type generated by protoc
type X int32
//enum constants generated by protoc
const (
X_A X = 0
X_B X = 1
X_C X = 2
X_D X = 3
)
func intToX(v int) (X, error) {
x := X(v)
switch x {
case X_A, X_B, X_C, X_D:
return x, nil
}
return 0, errors.New("could not convert int to X")
}
func main() {
for i := -1; i < 10; i++ {
if x, err := intToX(i); err != nil {
fmt.Println("unhandled error:", err, "for input value", i)
} else {
fmt.Printf("%d => X(%d)\n", i, x)
}
}
}
问题:是否有更好、更惯用的方法将传入的整数值映射到协议生成的常量?
特别是,我想避免在 case A, B, C, D
语句中明确列出所有常量。
我不知道你用的是哪个原型生成包,但是github.com/golang/protobuf/proto
你也可以得到枚举的反向映射。
示例 xyz.pb.go 生成的文件:
type TimeInterval int32
const (
TimeInterval_TI_UNKNOWN TimeInterval = 0
TimeInterval_TI_HOUR TimeInterval = 1
TimeInterval_TI_DAY TimeInterval = 2
TimeInterval_TI_WEEK TimeInterval = 3
TimeInterval_TI_MONTH TimeInterval = 4
TimeInterval_TI_QUARTER TimeInterval = 5
TimeInterval_TI_YEAR TimeInterval = 6
)
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
var TimeInterval_value = map[string]int32{
"TI_UNKNOWN": 0,
"TI_HOUR": 1,
"TI_DAY": 2,
"TI_WEEK": 3,
"TI_MONTH": 4,
"TI_QUARTER": 5,
"TI_YEAR": 6,
}
func (x TimeInterval) String() string {
return proto.EnumName(TimeInterval_name, int32(x))
}
func (TimeInterval) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
因此,您可以通过以下方式测试是否存在:
if _, found := TimeInterval_name[testinputint]; found{
//all ok
} else {
//not a value for this enum
}
是的,@RickyA 提到使用范围很好,因为它可以验证所有可能的基础常量值。
此外,您还可以检查枚举的长度,尽管这只有在基础枚举值没有任何 'gaps' 并且具有相应的数字范围时才有可能。
代码详细解释:
typelength := int32(len(TimeInterval_name))
if testinputint < 0 || int32(testinputint) >= typelength {
// not a value for this enum, return err
}
不那么冗长,只是使用 int 而不是 int32
if testinputint < 0 || int(testinputint) >= len(TimeInterval_name) {
// not a value for this enum, return err
}
但如前所述,这仅对遵守适当 iota 的枚举有效。当您将枚举更改为读取如下内容时,情况可能并非如此:
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
// we do not use month anymore 4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
因为生成的地图的长度会明显小于六:)
也就是说使用上面@Ricky_A找到的方法以防万一