100 Go Mistakes and How to Avoid Them 之代码结构和项目组织(一)
发布时间
阅读量:
阅读量
来源《100 Go Mistackes:How to Avoid Them》
一 意外的变量隐藏
变量的有效范围是指其可见程度。换句话说,在程序设计中,名称的有效部分通常受到特定限制。在Go语言中,声明在函数或方法块内的变量可以在内部嵌套的块(如子函数)中再次声明。这种称为'变量隐藏'的原则容易导致逻辑错误。
举例如下:
var client *http.Client 1
if tracing {
client, err := createClientWithTracing() 2
if err != nil {
return err
}
log.Println(client)
} else {
client, err := createDefaultClient() 3
if err != nil {
return err
}
log.Println(client)
}
// Use client
AI助手
在第一行先声明了一个名为client的变量。接着,在两个嵌套的代码块内使用了短变量声明运算符::=。这个运算符与上文中使用的名称相同,并生成了一个新的、独立的client变量。它不会为第一行定义的那个client变量赋值。因此,在该示例中HTTP客户端始终会保持为nil值。(注:在Golang中执行时会遇到这个问题)
如何解决这个问题呢:
var client *http.Client
if tracing {
c, err := createClientWithTracing()
if err != nil {
return err
}
client = c
} else {
// Same logic
}
AI助手
为外部命名空间命名为该名称,并将在内部用其他临时变量替代;最后将该临时名称重新指配给外部命名空间中的client。
最基本的做法就是在内部对该变量进行赋值操作即可(无需重新声明其他变量)
var client *http.Client
var err error
if tracing {
client, err = createClientWithTracing()
if err != nil {
return err
}
} else {
// Same logic
}
AI助手
最终这个代码可以优化成如下结构:
if tracing {
client, err = createClientWithTracing()
} else {
client, err = createDefaultClient()
}
if err != nil {
// Common error handling
}
AI助手
二 不必要的嵌套代码
func join(s1, s2 string, max int) (string, error) {
if s1 == "" {
return "", errors.New("s1 is empty")
} else {
if s2 == "" {
return "", errors.New("s2 is empty")
} else { -------(1)
concat, err := concatenate(s1, s2)
if err != nil {
return "", err
} else {
if len(concat) > max {
return concat[:max], nil
} else {
return concat, nil
}
}
}
}
}
func concatenate(s1 string, s2 string) (string, error) {
// ...
}
AI助手
在编号为(1)的这个位置,这个else的判断可以去掉,修改如下:
func join(s1, s2 string, max int) (string, error) {
if s1 == "" {
return "", errors.New("s1 is empty")
}
if s2 == "" {
return "", errors.New("s2 is empty")
}
concat, err := concatenate(s1, s2) // 这里避免了else判断
if err != nil {
return "", err
}
if len(concat) > max {
return concat[:max], nil
}
return concat, nil
}
func concatenate(s1 string, s2 string) (string, error) {
// ...
}
AI助手
这样的话,在编辑器里面看到的效果可能就如下所示:

代码结构如下的:
if foo() {
// ...
return true
} else {
// ...
}
AI助手
可以修改成下面这样的:
if foo() {
return true
}
// ...
AI助手
三 误用初始化方法( init functions)
在Go语言中,默认情况下是第一个被调用的方法(函数),其主要作用是初始化相关参数或配置信息。
package main
import "fmt"
var a = func() int {
fmt.Println("var")
return 0
}()
func init() {
fmt.Println("init")
}
func main() {
fmt.Println("main")
}
AI助手
得到的结果是:
var
init
main
AI助手
2 init执行顺序问题
main/main.go
package main
import (
"fmt"
"redis"
)
func init() {
// ...
}
func main() {
err := redis.Store("foo", "bar")
// ...
}
AI助手
redis/redis.go
package redis
// imports
func init() {
// ...
}
func Store(key, value string) error {
// ...
}
AI助手
在main目录下和redis目录下,均有init方法,执行顺序如下:

3 init方法不能嵌套在其他方法里面
package main
func init() {}
func main() {
init()
}
AI助手
编译后的结果
$ go build .
./main.go:6:2: undefined: init
AI助手
4 什么时候开始使用
一般用来初始化配置或者数据库链接校验等
var db *sql.DB
func init() {
dataSourceName := os.Getenv("MYSQL_DATA_SOURCE_NAME")
d, err := sql.Open("mysql", dataSourceName)
if err != nil {
log.Panic(err)
}
err = d.Ping()
if err != nil {
log.Panic(err)
}
db = d
}
AI助手
全部评论 (0)
还没有任何评论哟~
