Go表达式引擎expr基础用法实战指南

 更新时间:2025年10月23日 09:45:17   作者:水淹萌龙  
expr是一个高性能的 Go 表达式引擎,它允许你在代码中安全地执行动态生成的表达式,本文给大家介绍Go表达式引擎expr基础用法实战指南,感兴趣的朋友跟随小编一起看看吧

玩转 Go 表达式引擎:expr 实战指南

在日常开发中,我们经常需要处理动态表达式计算、业务规则判断等场景。Go 标准库虽然提供了模板引擎的条件判断,但面对复杂逻辑时显得力不从心。今天要介绍的 expr 库,正是为解决这类问题而生的轻量级表达式引擎。

什么是 expr?

expr 是一个高性能的 Go 表达式引擎,它允许你在代码中安全地执行动态生成的表达式。其语法接近 Go 语言,支持变量访问、函数调用、逻辑运算等,非常适合以下场景:

  • 动态业务规则判断
  • 配置化条件过滤
  • 模板中的动态计算
  • 复杂逻辑的动态执行

快速入门:第一个 expr 程序

先从最简单的表达式计算开始,比如计算 1+10 的结果:

package main
import (
	"fmt"
	"github.com/expr-lang/expr"
)
func main() {
	// 定义表达式
	code := "1 + 10"
	// 直接执行表达式(无需环境变量)
	result, err := expr.Eval(code, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出:11
}

这段代码展示了 expr 的核心用法:通过 expr.Eval() 函数直接执行表达式字符串,第一个参数是表达式,第二个参数是执行环境(可以是结构体、map 等)。

访问数据:从简单到复杂

expr 最强大的功能之一是能够访问外部数据,支持多种数据类型和嵌套结构。

1. 访问结构体字段

type User struct {
	Name string
	Age  int
}
func main() {
	user := User{Name: "Alice", Age: 25}
	// 表达式访问结构体字段
	code := `Name == "Alice" && Age > 18`
	result, _ := expr.Eval(code, user)
	fmt.Println(result) // 输出:true
}

注意:结构体字段必须首字母大写(导出字段),否则 expr 无法访问(受 Go 反射机制限制)。

2. 访问 Map 键值

func main() {
	data := map[string]interface{}{
		"product": "phone",
		"price":   3999,
	}
	// 两种访问方式等价
	code := `product == "phone" && .price > 3000`
	result, _ := expr.Eval(code, data)
	fmt.Println(result) // 输出:true
}

3. 访问嵌套结构

对于嵌套的结构体或 Map,使用 . 符号链式访问:

func main() {
	data := map[string]interface{}{
		"user": map[string]interface{}{
			"name": "Bob",
			"address": map[string]interface{}{
				"city": "Beijing",
			},
		},
	}
	// 访问嵌套 Map 的属性
	code := `user.name == "Bob" && user.address.city == "Beijing"`
	result, _ := expr.Eval(code, data)
	fmt.Println(result) // 输出:true
}

4. 处理切片和数组

结合 len() 函数可以方便地处理切片:

func main() {
	data := map[string]interface{}{
		"tags": []string{"go", "expr", "template"},
	}
	// 判断切片长度并访问元素
	code := `len(tags) == 3 && tags[0] == "go"`
	result, _ := expr.Eval(code, data)
	fmt.Println(result) // 输出:true
}

高级特性:让表达式更强大

1. 空安全访问

当访问可能为 nil 的字段时,使用 ?. 避免 panic:

code := `user?.address?.city == "Shanghai"`
// 如果 user 或 address 为 nil,表达式返回 false 而非错误

2. 自定义函数

expr 支持注册自定义函数,扩展表达式能力:

func main() {
	// 定义自定义函数:计算平方
	square := func(x int) int {
		return x * x
	}
	// 注册函数并执行表达式
	env := map[string]interface{}{
		"square": square,
		"num":    5,
	}
	code := `square(num) == 25`
	result, _ := expr.Eval(code, env)
	fmt.Println(result) // 输出:true
}

3. 编译优化

对于需要多次执行的表达式,先编译再执行可以提升性能:

func main() {
	code := `a + b * c`
	data := map[string]int{"a": 1, "b": 2, "c": 3}
	// 编译表达式
	program, _ := expr.Compile(code, expr.Env(data))
	// 多次执行(适合循环场景)
	for i := 0; i < 3; i++ {
		result, _ := expr.Run(program, data)
		fmt.Println(result) // 均输出:7
	}
}

结合模板引擎:动态渲染内容

虽然 expr 不是模板引擎,但可以与 Go 标准模板配合,实现动态内容渲染:

package main
import (
	"os"
	"text/template"
	"github.com/expr-lang/expr"
)
func main() {
	// 定义包含表达式的模板
	tplContent := `
	计算结果:{{calc "a + b * 2"}}
	判断结果:{{if calc "a > 10"}}a 大于 10{{else}}a 不大于 10{{end}}
	`
	// 准备数据
	data := map[string]int{"a": 5, "b": 3}
	// 创建模板并注册 calc 函数
	tpl := template.New("test").Funcs(template.FuncMap{
		"calc": func(exprCode string) (interface{}, error) {
			return expr.Eval(exprCode, data)
		},
	})
	// 解析并执行模板
	tpl.Parse(tplContent)
	tpl.Execute(os.Stdout, nil)
}

输出结果:

    计算结果:11
    判断结果:a 不大于 10

总结

expr 作为一款轻量级表达式引擎,以其接近 Go 的语法、良好的性能和丰富的特性,成为处理动态表达式场景的理想选择。无论是简单的数值计算,还是复杂的业务规则判断,expr 都能胜任。

通过本文介绍的基础用法和高级特性,相信你已经掌握了 expr 的核心能力。在实际项目中,还可以结合配置文件、数据库存储等方式,实现更灵活的动态规则系统。

如果你需要处理复杂的业务规则引擎场景,expr 绝对值得一试!

到此这篇关于Go表达式引擎expr基础用法实战指南的文章就介绍到这了,更多相关go表达式引擎expr 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang中的map操作方法详解

    Golang中的map操作方法详解

    这篇文章主要给大家介绍了关于Golang中map操作方法的相关资料,map是一种无序的基于key-value的数据结构,Go语言中map是引用类型,必须初始化才能使用,需要的朋友可以参考下
    2023-11-11
  • Go语言实现Kafka消息队列的示例代码

    Go语言实现Kafka消息队列的示例代码

    本文主要介绍了Go语言实现Kafka消息队列的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-07-07
  • Go语言题解LeetCode下一个更大元素示例详解

    Go语言题解LeetCode下一个更大元素示例详解

    这篇文章主要为大家介绍了Go语言题解LeetCode下一个更大元素示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Go官方工具链用法详解

    Go官方工具链用法详解

    Go官方工具链工具要求所有的Go源代码文件必须以.go后缀结尾。这里,我们假设一个最简单的Go程序放在hello.go的文件中,下面通过示例代码给大家介绍Go官方工具链用法简介,需要的朋友可以参考下
    2021-10-10
  • Golang中rune和byte的使用与区别

    Golang中rune和byte的使用与区别

    rune和byte都是Go语言中表示单个字符的类型,本文就来介绍一下Golang中rune和byte的使用与区别,具有一定的参考价值,感兴趣的可以了解一下
    2025-02-02
  • Go数据结构之HeapMap实现指定Key删除堆

    Go数据结构之HeapMap实现指定Key删除堆

    这篇文章主要给大家介绍了Go语言数据结构之HeapMap实现指定Key删除堆,通过使用Go语言中的container/heap包,我们可以轻松地实现一个优先级队列,文中有详细的代码示例讲解,需要的朋友可以参考下
    2023-07-07
  • 浅析Go语言中闭包的定义与使用

    浅析Go语言中闭包的定义与使用

    闭包是编程语言中的一个重要概念,它允许函数不仅仅是独立的代码块,还可以携带数据和状态,本文将深入探讨闭包的定义、用途和注意事项,以及如何正确使用闭包,有需要的可以参考下
    2023-09-09
  • Go语言开发k8s之Service操作解析

    Go语言开发k8s之Service操作解析

    这篇文章主要为大家介绍了Go语言开发k8s之Service操作解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Go语言实现的可读性更高的并发神库详解

    Go语言实现的可读性更高的并发神库详解

    这篇文章主要为大家介绍了Go语言实现的可读性更高的并发神库详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • Go1.21新增slices包的用法详解

    Go1.21新增slices包的用法详解

    Go 1.21新增的 slices 包提供了很多和切片相关的函数,可以用于任何类型的切片,这篇文章主要来和大家介绍一下slices包中相关函数的用法,需要的可以参考一下
    2023-08-08

最新评论