Go 限制函数参数

假设我们现在正在编写一个提供给外部使用的函数名 Fn

1
2
3
func Fn(a int)  {
// do something
}

Fn 接受一个 int 型的值,要求 a 只能是 1, 3 ,5 其中的一个,该如何去限制呢。

最简单的方式就是增加一段判断,在不符合要求时返回错误,就像下面这样。

1
2
3
4
5
6
7
8
9
func IPAddr(a int) error {
switch a {
case 1,3,5:
default:
return errors.New("a should be 1,3,5")
}
// do some
return nil
}

但是这样的处理不仅只能在运行时检查,而且新增了错误返回,外部调用时需要检查 err。我们不喜欢 if err != nil {}, 也不想在新增一种情况时,就在 case 后面新增一个值,直到它变成一条贪吃蛇。

那有没有什么办法能够在编译时就能检查出给定的值是否符合要求。

答案是有的,就像下面这段代码这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type enum struct {
i int
}

var (
One = enum{1}
Three = enum{3}
Five = enum{5}
)

func Fn(a enum) {
// do something
// a.i
}

在上面的代码中,我们定义了一个私有的类型 enum, 将 Fn 的参数类型设置为它。同时定义了 One, Three, Five 三个变量来代表 1, 3,5 这三个值,在 Fn 中获取 enum.i 来获取对应的值。

由于 enum 是私有类型,在外部不可以使用,在调用 Fn 时只能使用我们定义好的 One, Three, Five 来传递参数。通过这种方式,我们将函数的参数的范围限制在了 我们想要的范围内,同时避免了增加额外的逻辑判断。在编译时就能确定调用的方式是否正确。

可能你想问,这里为什么将 enum 定义为 struct, 而不是 int 的衍生类型。这是因为,如果我们将 enum 定义为 int ,调用方是可以通过直接传递字面量来绕过我们的限制的。就像下面这样

1
2
3
4
5
6
7
8
type enum2 int

var EOne enum2 = 1

func Fn2(a enum2) {
Fn2(EOne)
Fn2(7)
}

运行这段代码,会打印出

1
2
1
7

这并不是我们想要的结果,因此我们将 enum 定义为 struct, 而不是 int,从而防止调用方通过传递字面量的方式来绕过我们的检查。


Go 限制函数参数
https://blog.zhangliangliang.cc/post/limit-param-range-in-go.html
作者
Bobby Zhang
发布于
2023年11月30日
许可协议