
govaluate 是目前 Go 生态中稳定、轻量且语法最接近 C/JS 的表达式求值库,适合做规则引擎、告警阈值、权限判断等动态条件场景。它不执行任意 Go 代码,只解析和计算表达式,安全性可控,但变量绑定方式和类型处理稍有门槛——用错就 panic 或返回意外结果。
govaluate.NewEvaluableExpression 解析失败的常见原因
表达式字符串语法错误不会在 NewEvaluableExpression 阶段立刻暴露,而是在后续 Evaluate 时才报错(比如 unexpected token)。真正容易被忽略的是:空格、括号不匹配、未声明变量名拼写错误,或用了不支持的符号(如 ?? 在旧版不支持)。
- 确保表达式是纯表达式,不能含语句(如
if、for、函数定义) - 避免使用 Go 关键字作变量名(如
type、range),否则解析会失败 - 字符串字面量必须用双引号
"hello",单引号会被当成 rune 导致解析失败 - 如果表达式含点号访问(如
user.age),需确认传入的参数是 map 或 struct,且字段可导出
map[string]interface{} 绑定变量时的类型陷阱
govaluate 对参数类型敏感,尤其在比较、算术运算中。例如 "foo == 1" 中,若 parameters["foo"] 是 int64 而不是 int 或 float64,多数情况能自动转换;但遇到 == 比较字符串和数字,或 / 除法时,类型不一致会导致 nil 结果或 panic。
- 统一用
float64表示数值型参数(尤其是来自 JSON 解析的数字),避免整型溢出或隐式截断 - 布尔值必须传
true/false,不能传字符串"true" - 结构体字段访问要求字段名首字母大写(可导出),且不能是嵌套指针(如
user.*.name不支持) - 空值(
nil)参与运算会触发错误,建议提前用??操作符提供默认值,如user.score ?? 0 >= 90
带自定义函数的表达式必须用 NewEvaluableExpressionWithFunctions
想在表达式里调用 sum(a,b) 或 in_list(x, list) 这类函数,不能靠往 parameters 里塞闭包来实现——那样只会被当普通变量,调用时报 function not found。
立即学习“go语言免费学习笔记(深入)”;
- 必须用
govaluate.NewEvaluableExpressionWithFunctions创建表达式对象 - 函数签名固定为
func(args ...interface{}) (interface{}, error),返回值和错误都不可省略 - 函数体内需自行做类型断言,例如
args[0].(float64),失败则 return error - 函数名区分大小写,且不能与内置函数(如
len、abs)冲突
性能与并发安全注意事项
NewEvaluableExpression 是解析阶段,耗时相对高;Evaluate 是执行阶段,轻量且可并发调用。但表达式对象本身不是 goroutine-safe 的——多个 goroutine 同时调用同一个 expression.Evaluate 可能导致 panic(尤其含自定义函数时)。
- 推荐预编译:对固定规则字符串,只调用一次
NewEvaluableExpression,复用 expression 实例 - 每个 goroutine 独立调用
Evaluate,不要共享 parameters map(避免竞态) - 高频场景下,避免在表达式中频繁调用复杂自定义函数,会显著拖慢吞吐
- 如果规则来自用户输入,务必加超时控制(用 context 包封装 Evaluate 调用),防止恶意长循环表达式(如
while(true){}不支持,但深度递归或大数组遍历仍可能卡住)
最常被跳过的细节是:参数 map 的 key 必须严格匹配表达式里的变量名,大小写、下划线、点号都不能差;还有就是没意识到 Evaluate(nil) 和 Evaluate(map[string]interface{}{}) 行为不同——前者允许无变量表达式,后者会尝试查空 map,遇到未定义变量直接报错。


















