使用 go 变量时踩了个坑

72 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

之前在项目中遇到一个 bug,问题来源于没有真正理解局部变量。

我们一起来看下当时的场景:

现象

我尽量缩减一下代码,并保持了原有的主要逻辑。

package main

import (
	"encoding/json"
	"fmt"
	"time"

	redis "github.com/go-redis/redis/v7"
)

var client = redis.NewClient(&redis.Options{
	Addr:     "localhost:6379",
	Password: "123456",
})

func main() {
	m, err := GetModels(1)
	fmt.Printf("model=%+v, err=%v", m, err)
}

type model struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

func GetModels(id int) (model, error) {
	key := "model"
	cache, err := client.Get(key).Bytes()
	var res model
	if err != nil {
		fmt.Println("key not exists")
		res, err := GetFromDB(id)
		if err != nil {
			return res, err
		}
		value, err := json.Marshal(res)
		if err != nil {
			return res, err
		}
		client.Set(key, value, 2*time.Minute)
	} else {
		json.Unmarshal(cache, &res)
	}
	return res, nil
}

func GetFromDB(id int) (res model, err error) {
	res = model{1, "13sai"}
	return
}

代码很简单,先查 redis,redis 中key miss,查 db 并写 redis,返回结果。

本地测试。

 var git:(master) ✗ go run main.go
key not exists
model={Id:0 Name:}, err=<nil>%                                                                                                         
➜  var git:(master) ✗ go run main.go
model={Id:1 Name:13sai}, err=<nil>%    

发现没有命中redis时,res 总是初始化的值,这可不是我希望的返回。

Test

看到这里,不妨仔细看看上面的代码,看看你能不能发现 bug 所在吗?



如果不知道,name你也对局部变量比较模糊。

我们写个🌰

package tests

import (
	"errors"
	"testing"
)

func TestVar(t *testing.T) {
	var a int64
	btn := true
	if btn {
		a, err := 2, errors.New("13sai")
		t.Log("btn", a, err)
	}

	t.Log(a)
}

运行一下,output:

➜  var git:(master) ✗ go test -v var_test.go 
=== RUN   TestVar
    var_test.go:13: btn 2 13sai
    var_test.go:16: 0
--- PASS: TestVar (0.00s)
PASS

why???

从上面的输出结果可以清晰地看出来,因为使用了 := 的缘故,变量在 if 内部才有效,不影响 if 外部变量,修改也就简单了:

...
res, err = service.GetFromDB(id)
...

去掉冒号即可。

我们修改后运行:

➜  var git:(master) ✗ go run main.go
key not exists
model={Id:1 Name:13sai}, err=<nil>%                                                                                                    
➜  var git:(master) ✗ go run main.go
model={Id:1 Name:13sai}, er                                                 

可以看到 redis 中 key 不存在,返回结果也正常,解决了上述 bug。

这是使用go的:=需要注意的点,变量作用域需要注意哦!如果经常使用 := 声明变量,很容易忽视这个错误。特别是逻辑复杂,比如我使用的场景,引入了 redis cache,很难发现到。

PS: := 只能局部定义,不能在function外定义。

相关代码参考:github.com/13sai/blog-…