Go Context 到底放第一个参数传,还是放结构体里?

大家好,我是煎鱼。

前段时间我们在聊手动管理内存 arena 的后续时。我们有提到 context 的函数传参等问题。

当时在评论区有许多的小伙伴交流了起来,大家对此还是非常关注的:

今天我们就来聊一聊 conetxt 传参的这个事。到底摆哪?Go 官方推荐是什么?

快速介绍

上下文(Context)是 Go 语言中非常有特色的一个特性,其主要的作用是在 goroutine 中进行上下文的传递,而在传递信息中又包含了 goroutine 的运行控制、上下文信息传递等功能。

以下是一个简单 Demo:

go 复制代码
func main() {
	parentCtx := context.Background()
	ctx, cancel := context.WithTimeout(parentCtx, 1*time.Millisecond)
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		f1(ctx)
	case <-ctx.Done():
		f2(ctx)
	}
}

func f1(ctx context.Context) {
	fmt.Println("脑子进煎鱼了", ctx.Err())
}

func f2(ctx context.Context) {
	fmt.Println("煎鱼进脑子了", ctx.Err())
}

输出结果:

复制代码
煎鱼进脑子了 context deadline exceeded

Go context 放哪好的争议点

根据上面的 Demo,有争议的点在哪?就是这个函数里 context,到底怎么传。放函数第一个参数,还是放结构体?

go 复制代码
// 第一种方式
func f1(ctx context.Context) {...}

// 第二种方式
type T struct {
  Ctx context
}
  • 放在函数第一个参数 context 中的话,那每个方法都需要进行传递,用起来就像被 "污染" 了一样。每个函数都要层层递进的传下去。非常的显性。
  • 放在结构体中的话,按理说 context 是基本对标请求的,每次请求都是不同的 context。这样可能会导致每次都要 NewT() 或是 WithContext。一旦忘了,就很容易埋下隐患。

Go 官方建议

Go 官方建议中是怎么说的?在 context 标准库的文档中有提到。使用上下文的程序应遵循以下规则,以保持跨包的接口一致:

  1. 不要将 context 存储在结构体中。
  2. 应该将 context 显式传递给每个需要它的函数,其应该是第一个参数,通常将参数命名为 ctx。
  3. 不要传递 nil context。如果您不确定要使用哪个 context,请传递 context.TODO

对应的示例代码:

go 复制代码
func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

划重点:官方建议在函数首位参数传递 context,不建议使用结构体传递。

一些讨论

虽然官方是这么建议了。但看起来没头没尾,不认可的人也有不少。

包括前 Go 核心团队的 @Brad Fitzpatrick 也表达了反对的意见,并给出进一步的诠释:

While we've told people not to add contexts to structs, I think that guidance is over-aggressive. The real advice is not to store contexts. They should be passed along like parameters. But if the struct is essentially just a parameter, it's okay. I think this concern can be addressed with package-level documentation and examples.

大概意思:我们曾告诉人们不要在结构体中添加上下文,但我认为这种指导过于激进。真正的建议是不要存储上下文。它们应该像参数一样传递。但如果结构体本质上只是一个参数,那就没有问题。我认为可以通过包级文档和示例来解决这个问题。

可能有同学想说,那是不是 Go 官方就可以放宽 context 的使用建议了?

想太多了...早在 2017 年就有 @Caleb Spare 提出《context: relax recommendation against putting Contexts in structs》,希望 Go 官方放宽对于 context 放入结构体中的建议。

但还是处于长期的阻塞状态,没有进一步的相关信息。@Russ Cox 被 @ 了也并没有出来说任何的建议。

可以认为官方文档上的态度没有什么变化。

总结

Context 在 Go 中是一个非常重要的特性,基本覆盖编程的方方面面,只要和 "控制" 相关的,都有他的存在。

在官方的建议中,是不推荐使用结构体存储 context 的,更推荐使用函数第一个参数传递 context 并命名为 ctx。进一步诠释的话,指的是不希望存储 context。

大家在 context 实践中又是使用哪种方式呢?欢迎分享。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
chxii1 小时前
18.2.go语言redis中使用lua脚本
redis·go·lua
用户0142260029842 小时前
Go语言 Map 详解
go
孔令飞3 小时前
Go 1.24 中的弱指针包 weak 使用介绍
人工智能·云原生·go
我的golang之路果然有问题16 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
M1A120 小时前
云原生第一步:Windows Go环境极速配置
后端·go
纪元A梦1 天前
华为OD机试真题——推荐多样性(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
K8sCat1 天前
Golang与Kafka的五大核心设计模式
后端·kafka·go
孔令飞1 天前
Go:终于有了处理未定义字段的实用方案
人工智能·云原生·go
唐僧洗头爱飘柔95271 天前
(Go Gin)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
后端·golang·go·restful·gin·goweb开发
ん贤1 天前
并发编程【深度解剖】
后端·go·并发