Go Context 到底放第一個參數傳,還是放結構體裏?

大家好,我是煎魚。

前段時間我們在聊手動管理內存 arena 的後續時。我們有提到 context 的函數傳參等問題。

當時在評論區有許多的小夥伴交流了起來,大家對此還是非常關注的:

今天我們就來聊一聊 conetxt 傳參的這個事。到底擺哪?Go 官方推薦是什麼?

快速介紹

上下文(Context)是 Go 語言中非常有特色的一個特性,其主要的作用是在 goroutine 中進行上下文的傳遞,而在傳遞信息中又包含了 goroutine 的運行控制、上下文信息傳遞等功能。

以下是一個簡單 Demo:

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,到底怎麼傳。放函數第一個參數,還是放結構體?

// 第一種方式
func f1(ctx context.Context) {...}

// 第二種方式
type T struct {
  Ctx context
}

Go 官方建議

Go 官方建議中是怎麼說的?在 context 標準庫的文檔中有提到。使用上下文的程序應遵循以下規則,以保持跨包的接口一致:

  1. 不要將 context 存儲在結構體中。

  2. 應該將 context 顯式傳遞給每個需要它的函數,其應該是第一個參數,通常將參數命名爲 ctx。

  3. 不要傳遞 nil context。如果您不確定要使用哪個 context,請傳遞 context.TODO

對應的示例代碼:

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[1]》,希望 Go 官方放寬對於 context 放入結構體中的建議。

但還是處於長期的阻塞狀態,沒有進一步的相關信息。@Russ Cox 被 @ 了也並沒有出來說任何的建議。

可以認爲官方文檔上的態度沒有什麼變化。

總結

Context 在 Go 中是一個非常重要的特性,基本覆蓋編程的方方面面,只要和 “控制” 相關的,都有他的存在。

在官方的建議中,是不推薦使用結構體存儲 context 的,更推薦使用函數第一個參數傳遞 context 並命名爲 ctx。進一步詮釋的話,指的是不希望存儲 context。

大家在 context 實踐中又是使用哪種方式呢?歡迎分享。

參考資料

[1]

context: relax recommendation against putting Contexts in structs: https://github.com/golang/go/issues/22602

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DZl_lIueSHpdjfWJU9RrHg