开启掘金成长之旅!这是我参与「掘金日新计划 · 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-…