关于golang:你会犯这些-Go-编码错误吗二

60次阅读

共计 2753 个字符,预计需要花费 7 分钟才能阅读完成。

大家好,我是煎鱼。

前一次给大家分享了《你会犯这些 Go 编码谬误吗(一)?》,不晓得大家排汇的怎么样,还有再踩到相似的坑吗?

明天持续来第二弹,跟煎鱼上车。

Go 常见谬误

6. 同名变量的作用域

问题

咱们在编写程序时,因为各种长期变量,会罕用变量名 n、i、err 等。有时候会遇到一些问题,如下代码:

func main() {
    n := 0
    if true {
        n := 1
        n++
    }
    fmt.Println(n)
}

程序的输入后果是什么。n 是 1,还是 2?

输入后果:

0

解决办法

上述代码的 n := 1 又从新申明了一个新变量,他是同名变量,同时很要害的,他蕴含同名局部变量。

咱们的 n++ 影响的是 if 区块里的变量 n,而不是内部的同名变量 n。如果要正确影响,该当批改为:

func main() {
    n := 0
    if true {
        n = 1
        n++
    }
    fmt.Println(n)
}

输入后果:

2

这一个案例尽管繁多提出来看并不简单,但在许多 Go 初学者刚入门时常常会在应用程序中遇到相似的问题,而后问为什么不行 …

据鱼的 7s 记忆,在全局 DB 句柄中等场景中遇到这个问题,来问煎鱼的应该有 10 次以上,是一个比拟高频的”坑“了。

7. 循环中的长期变量

问题

置信不少同学在业务代码中做过相似的事件,那就是:边循环解决业务数据,边变更值内容。

如下代码:

s := []int{1, 1, 1}
for _, n := range s {n += 1}
fmt.Println(s)

程序输入的后果是什么,i 胜利均都 +1 了吗?

不,真正的输入后果是:[1 1 1]

解决办法

实际上在循环中,咱们所援用的变量是 长期变量,你去批改他是没有任何意义的,批改的基本不是你的原数据的构造。

咱们须要定位到原数据,依据索引去定位批改。如下代码:

s := []int{1, 1, 1}
for i := range s {s[i] += 1
}
fmt.Println(s)

输入后果:

[2 2 2]

这很常见,一个不留神就会手抖。

8. JSON 转换和输入为空

问题

在做对外接口的数据对接和转换时,咱们常常须要对 JSON 数据进行解决。

如下代码:

type T struct {
    name string
    age  int
}

func main() {p := T{"煎鱼", 18}
    jsonData, _ := json.Marshal(p)
    fmt.Println(string(jsonData))
}

输入后果是什么,能把姓名和年龄失常输入吗?

输入后果:

{}

你没有看错,程序的输入后果是空,没有转换到任何货色。

解决办法

起因是 JSON 的输入,只会输入公开(导出)字段,也就是首字母必须为大写。

咱们须要进行如下革新:

type T struct {
    Name string
    Age  int
}

func main() {p := T{"煎鱼", 18}
    jsonData, _ := json.Marshal(p)
    ...
}

输入后果:

{"Name":"煎鱼","Age":18}

又或是显式指定 JSON 的标签:

type T struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

当初 IDE 可能很不便的间接生成 JSON 标签了,倡议大家能够习惯性补上,确保字段标准。

真的能够防止不少的对接时的拉扯和误差。

9. 认为 recover 是万能的

问题

在 Go 中,goroutine + panic + recover 是天作之合,用起来很不便。经常会有同学认为他是万能的。

如下代码:

func gohead() {go func() {panic("煎鱼上班了")
    }()}

func main() {go func() {defer func() {if r := recover(); r != nil {fmt.Println(r)
            }
        }()

        gohead()}()

    time.Sleep(time.Second)
}

你认为输入后果是什么。程序被中断,还是胜利 recover 了?

输入后果:

panic: 煎鱼上班了

goroutine 17 [running]:
main.gohead.func1()
        /Users/eddycjy/awesomeProject/main.go:10 +0x39
created by main.gohead
        /Users/eddycjy/awesomeProject/main.go:9 +0x35

你认为 recover 是万能的?并不。

解决办法

Go1 现阶段没有万能解决办法,只能恪守 Go 的标准(Go 就是不能跨 goroutine recover,是正确的逻辑)。

Goroutine 倡议每个 goroutine 都须要有 recover 兜底,否则一旦呈现 panic 就会导致利用中断,容器会重启。

另外 Go 底层被动抛出的致命谬误 throw,是没有方法应用 recover 拦挡的,请务必留神。

注:有同学始终认为 recover 能够拦挡到 throw,特此说明。

10. nil 不是 nil

问题

咱们在做程序的逻辑解决时,常常要对接口(interface)值进行判断。

如下代码:

func Foo() error {
    var err *os.PathError = nil
    return err
}

func main() {err := Foo()
    fmt.Println(err)
    fmt.Println(err == nil)
}

你认为输入后果是什么,是 nil 和 true 吗?

输入后果:

<nil>
false

外表看起来的 nil,它并不等于 nil。

解决办法

接口值,是非凡的。只有当它的值和动静类型都为 nil 时,接口值才等于 nil。

在后面的问题代码中,实际上函数 Foo 返回的是 [nil, *os.PathError],咱们将其与 [nil, nil] 进行比拟,所以是 false。

如果要精确判断,要进行如下转换:

fmt.Println(err == (*os.PathError)(nil))

输入后果就会为 true。

另外就是尽量应用 error 类型,又或是防止与 interface 进行比拟,这是比拟危险的行为(有不少人不晓得这一景象)。

总结

在明天这篇文章中,咱们开始了 Go 常见编码谬误的第二节,共波及 5 个案例:

  1. 同名变量的作用域。
  2. 循环中的长期变量。
  3. JSON 转换和输入为空。
  4. 认为 recover 是万能的。
  5. nil 不是 nil。

这仍然是十分常见的案例,你有没有遇到过?或是有其它的新的案例呢?

欢送大家一起交换和沟通。

文章继续更新,能够微信搜【脑子进煎鱼了】浏览,本文 GitHub github.com/eddycjy/blog 已收录,学习 Go 语言能够看 Go 学习地图和路线,欢送 Star 催更。

举荐浏览

  • Go 语言入门系列:初探 Go 我的项目实战
  • Go 语言编程之旅:深刻用 Go 做我的项目
  • Go 语言设计哲学:理解 Go 的为什么和设计思考
  • Go 语言进阶之旅:进一步深刻 Go 源码

参考

  • 案例来自 Stefan Nilsson:Go gotchas
  • 案例来自 24 Common Mistakes in Go (gotchas) And How To Avoid Them

正文完
 0