Golang中哪种写法更符合语言习惯?

Golang中哪种写法更符合语言习惯? 这里存在两种惯用法之间的明显冲突:

  1. 逗号Ok惯用法与If语句:将变量的作用域限制在仅需要它们的地方,常用于错误处理和类型断言。
  2. 避免不必要的else:在if块以return语句结束时,避免使用else块,以减少嵌套并提高可读性。

逗号Ok惯用法

	if bs, err := json.Marshal(b); err != nil {
		handleError(w, err)
		return
	} else {
		fmt.Fprintln(w, string(bs))
	}

避免不必要的else

	var bs []byte
	if bs, err = json.Marshal(b); err != nil {
		handleError(w, err)
		return
	} 
	fmt.Fprintln(w, string(bs))

将“bs”移到if语句之外/之上会扩大其作用域,并可能导致错误。

Golint 支持第二种惯用法:

if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)

注意事项

  1. 上下文很重要。对于较小的函数,变量作用域的扩大不会显著增加复杂性或风险,此时通过避免不必要的else块来简化控制流可能是首选。在更复杂的函数中,或者当一个变量确实不应该在特定代码块之外被访问时,保持更严格的作用域可能被优先考虑。
  2. golint 基于常见的最佳实践提供建议,但它并不涵盖所有可能的代码上下文或细微差别。它是一个辅助开发的工具,而不是一套必须在所有情况下严格遵守的规则。

那么,Golint 是否因此成为权威答案,使得第二种选择是更地道的做法?


更多关于Golang中哪种写法更符合语言习惯?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

我通常这样写:

bs, err := json.Marshal(b)
if err != nil {
	handleError(w, err)
	return
}
fmt.Fprintln(w, string(bs))

更多关于Golang中哪种写法更符合语言习惯?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


根据我阅读Go代码的经验,这绝对是处理这类事情最常见的方式。

我仅在函数除了错误没有其他返回值时,才会使用方法1。

if err := doSomething(); err != nil {
  // 处理错误
}

说实话,我从未见过有人使用方法2。

Dean_Davidson:

这非常符合 Go 语言的惯用法,如果你查看 Go 的源代码,你会发现它随处可见。

谢谢!我怎么就没想到去标准库里找找例子呢?虽然里面也有一些相当奇特的例子(没错,sort 包,我说的就是你!)。

感谢回复!

这似乎是一种非常流行的做法。其好处在于,通过使用 bs 的简短声明,代码变得简单明了,并且无需查找和指定其类型(类似于我帖子中的选项 #1)。其代价是,这也扩大了 err 的作用域。

Massimo.Costa:

好吧,根据我阅读Go代码的经验,这绝对是处理这类事情最常见的方式。

谢谢!

这是我之前没有考虑到的一点。是否采用惯用方式取决于你是否需要对“逗号,Ok”惯用法中涉及的变量的返回值进行进一步处理。

这是一种非常地道的写法,如果你查看 Go 的源代码,会发现这种写法随处可见。例如在 encoding/json 中:

// convertNumber converts the number literal s to a float64 or a Number
// depending on the setting of d.useNumber.
func (d *decodeState) convertNumber(s string) (any, error) {
	if d.useNumber {
		return Number(s), nil
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeFor[float64](), Offset: int64(d.off)}
	}
	return f, nil
}

源代码

在Go语言中,第二种写法(避免不必要的else)更符合惯用法。Go社区普遍遵循“尽早返回”的模式,这有助于减少嵌套、提高代码可读性。golint的建议反映了这一共识,但工具本身并非绝对权威,代码的可维护性和清晰度才是关键。

示例代码:

// 更地道的写法
func handleData(w http.ResponseWriter, b interface{}) {
    bs, err := json.Marshal(b)
    if err != nil {
        handleError(w, err)
        return
    }
    fmt.Fprintln(w, string(bs))
}

如果需要在条件判断中初始化变量,可以这样写:

func processData(data interface{}) error {
    if bs, err := json.Marshal(data); err != nil {
        return err
    } else {
        // 使用bs
        _ = bs
    }
    return nil
}

但更推荐将变量声明移到if外部:

func processData(data interface{}) error {
    bs, err := json.Marshal(data)
    if err != nil {
        return err
    }
    // 使用bs
    _ = bs
    return nil
}

对于错误处理,Go的典型模式是:

value, ok := someMap[key]
if !ok {
    return errors.New("key not found")
}
// 使用value

这种模式将错误检查放在前面,正常逻辑放在后面,避免了深层嵌套。

回到顶部