Go 更強的程式碼潔癖,可以把 gofmt 給換了!

發表於2024-02-21

大家好,我是煎魚。

我們從一開始寫 Go 程式碼和應用,就會被各種官方和民間教程,甚至 IDE 教導我們必須配一個 Gofmt 工具。他能夠格式化 Go 程式的程式碼。會使用製表符表示縮排,空白表示對齊。

這解決了程式設計師屆的老大難問題之一,程式碼格式上的規範問題。有效的提高了 Go 程式碼的閱讀的友好度和減少了同事間的 **。非常值得認可。

但有時候,還是會看到一些糟心的程式碼,總會覺得 Gofmt,還是格式化的不夠。

今天給大家分享我發現的一個更狠的工具:gofumpt,例子主要基於官方檔案。

更強的格式化:gofumpt

Gofumpt 會執行比 gofmt 更嚴格的 Go 格式規範。同時確保向後相容。

該工具是 Go 1.21 的 gofmt 分支,需要 Go 1.20 或更高版本。它可以直接替代現有的 Go 程式碼格式化,也就是在 gofumpt 之後執行 gofmt 不會產生任何新的變化。

安裝命令:

$ go install mvdan.cc/gofumpt@latest

執行命令:

$ gofumpt -l -w .
main.go

再檢視對應被格式化的檔案就已經生效了。

以下是一些更具體的 gofmt 和 gofumpt 的區別例子。能夠很好的幫助大家識別其中的差異。

賦值運運算元後無空行

原本由 gofmt 格式化後:

func foo() string {
    foo :=
        "腦子進煎魚了!"
    return foo
}

改為 gofumpt 格式化後:

func foo() string {
    foo := "腦子進煎魚了!"
    return foo
}

函式體周圍無空行

原本由 gofmt 格式化後:

func foo() {

    println("煎魚進腦子了!")

}

改為 gofumpt 格式化後:

func foo() {
    println("煎魚進腦子了!")
}

函式應分隔 ) { ,縮排有助於提高可讀性

原本由 gofmt 格式化後:

func foo(s string,
    i int) {
    println("煎魚!!!")
}

// 使用空行會稍微好一些,但仍然不夠好
func bar(s string,
    i int) {

    println("煎魚!!!")
}

改為 gofumpt 格式化後:

func foo(s string,
    i int,
) {
    println("煎魚!!!")
}

func bar(s string,
    i int,
) {
    println("煎魚!!!")
}

程式碼塊中的單獨語句(或註釋)周圍沒有空行

原本由 gofmt 格式化後:

if err != nil {

    return err
}

改為 gofumpt 格式化後:

if err != nil {
    return err
}

簡單錯誤檢查前無空行

原本由 gofmt 格式化後:

foo, err := processFoo()

if err != nil {
    return err
}

改為 gofumpt 格式化後:

foo, err := processFoo()
if err != nil {
    return err
}

複合字面量應統一使用換行符

原本由 gofmt 格式化後:

var ints = []int{1, 2,
    3, 4}

var matrix = [][]int{
    {1},
    {2}, {
        3,
    },
}

改為 gofumpt 格式化後:

var ints = []int{
    1, 2,
    3, 4,
}

var matrix = [][]int{
    {1},
    {2},
    {
        3,
    },
}

空欄位列表應使用單行

原本由 gofmt 格式化後:

var V interface {
} = 3

type T struct {
}

func F(
)

改為 gofumpt 格式化後:

var V interface{} = 3

type T struct{}

func F()

標準庫匯入必須在頂部單獨分組

原本由 gofmt 格式化後:

import (
    "foo.com/bar"

    "io"

    "io/ioutil"
)

改為 gofumpt 格式化後:

import (
    "io"
    "io/ioutil"

    "foo.com/bar"
)

短 case 子句應佔一行

原本由 gofmt 格式化後:

switch c {
case 'a', 'b',
    'c', 'd':
}

改為 gofumpt 格式化後:

switch c {
case 'a', 'b', 'c', 'd':
}

多行頂層宣告必須用空行隔開

原本由 gofmt 格式化後:

func foo() {
    println("煎魚!")
}
func bar() {
    println("煎魚!")
}

改為 gofumpt 格式化後:

func foo() {
    println("煎魚!")
}

func bar() {
    println("煎魚!")
}

單個 var 宣告不應使用括號分組

原本由 gofmt 格式化後:

var (
    foo = "煎魚!"
)

改為 gofumpt 格式化後:

var foo = "煎魚!"

連續的頂層宣告應歸為一組

原本由 gofmt 格式化後:

var nicer = "x"
var with = "y"
var alignment = "z"

改為 gofumpt 格式化後:

var (
    nicer     = "x"
    with      = "y"
    alignment = "z"
)

簡單的 var 宣告語句應使用短賦值

原本由 gofmt 格式化後:

var s = "煎魚進腦子了"

改為 gofumpt 格式化後:

s := "煎魚進腦子了"

預設啟用 -s 程式碼簡化標記。

非 Go 指令的註釋應以空格開頭

原本由 gofmt 格式化後:

//go:noinline

//Foo is awesome.
func Foo() {}

改為 gofumpt 格式化後:

//go:noinline

// Foo is awesome.
func Foo() {}

VSCode 配置

可以直接在 IDE 中進行配置。例如 VSCode,可以配置對應的 settings.json 為如下:

{
    "go.useLanguageServer": true,
    "gopls": {
        "formatting.gofumpt": true,
    },
}

就可以直接在 Go 應用中用起來了。

總結

之前我有一個朋友,接手了一個老專案。那位同學,幾乎沒有什麼程式碼規範的風格。全靠 gofmt 來幫他格式化。

但你們也看到,gofmt 只做了最基本的。這種時候如果有更嚴格的 Go 程式碼格式化工具 gofumpt 是非常不錯的。(也需要引導這位同學,但容易撕逼)

像是前面提到的 “多行頂層宣告必須用空行隔開” 是非常有價值的。我是真的見過一大坨不加空行都擠一起的。看起來非常難受。

希望這個更嚴格的 gofumpt,對大家格式化 Go 程式碼能有所幫助!

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

推薦閱讀

相關文章