Why your golang sucks:每个人都会踩的GO的五十个坑 (1-10)

Why your golang sucks:每个人都会踩的GO的五十个坑 (1-10)

1.本文是我在翻GO文章时发现的,译者主要完成了1-22条的翻译(有不少问题。),没了后续,本系列文章将会先转载并修订他最初的翻译,然后完成第23-50条的翻译

2.由于本篇文章最初写自2015年,而GO也在这两年间发生了较大变化,以下的50个坑将会重新实验修正,将会与原文稍微有些区别

3.本文翻译自 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

4.http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html

Go语言是一个简单却蕴含深意的语言。但是,即便号称是最简单的C语言,都能总结出一本《C陷阱与缺陷》,更何况Go语言呢。Go语言中的许多坑其实并不是因为Go自身的问题。一些错误你再别的语言中也会犯,例如作用域,一些错误就是对因为 Go 语言的特性不了解而导致的,例如 range。

其实如果你在学习Go语言的时候去认真地阅读官方文档,百科,邮件列表或者其他的类似 Rob Pike 的名人博客,报告,那么本文中提到的许多坑都可以避免。但是不是每个人都会从基础学起,例如译者就喜欢简单粗暴地直接用Go语言写程序。如果你也像译者一样,那么你应该读一下这篇文章:这样可以避免在调试程序时浪费过多时间。

本文将50个坑按照使用使用范围和难易程度分为以下三个级别:“新手入门级”,“新手深入级”,“新手进阶级”。

1.“{”不能单独放在一行

级别:新手入门级

Go语言设计者肯定和C语言设计者(K&R)有种不明不白的关系,因为C语言中的K&R格式在Go语言中得到发扬光大。大多数语言中,大括号中的左括号是可以随便放在哪里的:C语言中必须要按照K&R格式对代码进行格式化之后,左括号才会被放在前一行中的最后。但是Go语言中,左括号必须强制不能单独放在一行。这个规则得益于“自动分号注射”(automatic semicolon injection)。

补充:go提供了专门用于格式化代码的gofmt工具。

出错代码:

package main

import "fmt"

func main()  
{ //{不能单独一行
    fmt.Println("hello there!")
}

错误信息:

./main.go:6: syntax error: unexpected semicolon or newline before {

修正代码:

package main

import "fmt"

func main() {  
    fmt.Println("works!")
}

2.未使用已定义的变量

级别:新手入门级

如果代码中有未使用的变量,那个代码编译的时候就会报错。Go要求在代码中所有声明的变量都需要被用到,当然,全局变量除外。
函数的参数也可以只被声明,不被使用。

对于未声明变量的调用同样会导致编译失败。

出错代码:

package main

var gvar int //not an error

func main() {  
    var one int   //error, unused variable
    two := 2      //error, unused variable
    var three int //error, even though it's assigned 3 on the next line
    three = 3
}

错误信息:

./main.go:7: one declared and not used
./main.go:8: two declared and not used
./main.go:9: three declared and not used

修正代码:

错误的修正方式(手工赋值,规避掉错误)

package main

func main() {  
    var one int
    _ = one

    two := 2 
    fmt.Println(two)

    var three int 
    three = 3
    one = three

    var four int
    four = four
}

不用的变量就要删掉

package main
import "fmt"
var gvar int //not an error

func main() {  
    three := 3

    fmt.Println("three:", three)
}

3.未使用的包

级别:新手入门级

当import一个包之后,如果不使用这个包,或者这个包中的函数/接口/数据结构/变量,那么将会编译失败。

可以删掉该import引用,或者再包前面加上”_”符号

出错代码:

package main

import (  
    "fmt"
    "log"
    "time"
)

func main() {  
}

错误信息:

# command-line-arguments
./50demo.go:4: imported and not used: "fmt"
./50demo.go:5: imported and not used: "log"
./50demo.go:6: imported and not used: "time"

修正代码

package main

import (  
    _ "fmt"
    "log"
    "time"
)

var _ = log.Println

func main() {  
    _ = time.Now
}

对于”“符号的理解不要将其仅仅理解为解决未使用包的编译错误,import 引用该包 该包下的文件里所有init()函数都会被引用,其他的函数等并不会被引用进来,且无法通过包名来调用包中的其他函数。比如一个很常见的用法,import _ "github.com/go-sql-driver/mysql"

4.只能在函数内部使用精简声明赋值

级别:新手入门级

出错代码:

package main

myvar := 1 //error, 精简声明赋值

func main() {  
}

错误信息:

# command-line-arguments
./50demo.go:3: syntax error: non-declaration statement outside function body

修正代码:

package main

var myvar = 1

func main() {  
}

5.无法使用精简声明赋值语句对变量重新赋值

级别:新手入门级

不能使用精简的声明赋值语句重新赋值变量,但是可以使用精简的赋值语句同时声明赋值多个变量。

当然,声明的变量要在其作用域内使用,否则有变量未使用错误,或者可以用”_”符号来匿名该变量

出错代码:

package main

func main() {  
    one := 0
    one := 1 //error
}

错误信息:

# command-line-arguments
./50demo.go:5: no new variables on left side of :=

修正代码:

package main

func main() {  
    one := 0
    one, two := 1,2
    one,two = two,one


    _, three := 0, 3
    one = three
}

6.不能对结构体中的参数使用精简声明赋值

级别:新手入门级

出错代码:

package main

import (  
    "fmt"
)

type info struct {  
    result int
}

func work() (int,error) {  
    return 13,nil  
}

func main() {  
    var data info

    data.result, err := work() //error
    fmt.Printf("info: %+v\n",data)
}

错误信息:

# command-line-arguments
./50demo.go:18: non-name data.result on left side of :=

直白的来讲,无法声明data.result这样一个变量

修正代码:

package main

import (  
    "fmt"
)

type info struct {  
    result int
}

func work() (int,error) {  
    return 13,nil  
}

func main() {  
    var data info

    var err error
    data.result, err = work() //ok
    if err != nil {
    fmt.Println(err)
    return
    }

    fmt.Printf("info: %+v\n",data) //prints: info: {result:13}
}

7.注意局部变量作用域

级别:新手入门级

和 C 语言一样,Go 语言也有作用于,一个变量的作用范围仅仅是一个代码块。虽然精简的赋值语句很简单,但是注意作用域。

package main

import (
    "fmt"
)

func main() {  
    var x int = 1
    fmt.Println(x)     //打印 1

    a, x := 0, 3
    fmt.Println(x)     //打印 3
    fmt.Println(a)     //打印 0
    {
        fmt.Println(x) //打印 3
        a, x := 10, 30
        fmt.Println(x) //打印 30
        fmt.Println(a) //打印 10
    }
    fmt.Println(x)     //打印 3
}

在作者的这个例子之外,举一个go常用的回滚场景

8.无法用 nil 对不使用显式类型的变量赋值

级别:新手入门级

nil 可以用作 interface、function、pointer、map、slice 和 channel 的“空值”。但是如果不特别指定的话,Go 语言不能识别类型,所以会报错。

错误信息:

package main

func main() {  
    var x = nil //error

    _ = x
}

错误信息:

# command-line-arguments
./50demo.go:28: use of untyped nil

修正代码:

package main

func main() {  
    var x interface{} = nil

    _ = x
}

9.Slice 和 Map 的 nil 值

级别:新手入门级

初始值为 nil 的 Slice 是可以进行“添加”操作的,但是对于 Map 的“添加”操作会导致运行时panic。

正确代码:

package main

func main() {  
    var s []int  //nil
    s = append(s,1)   //ok
}

错误代码:

package main

func main() {  
    var m map[string]int   //nil
    m["one"] = 1 //panic, need alloc(make) at first

}

10.Map和cap()

级别:新手入门级

cap()函数返回的是数组切片分配的空间大小

创建 Map 的时候可以指定 Map 的长度,但是在运行时是无法使用 cap() 获取其容量

错误信息:

package main

func main() {  
    m := make(map[string]int,99)
    cap(m) //error
}

错误信息:

# command-line-arguments
./50demo.go:29: invalid argument m (type map[string]int) for cap

猜你喜欢

转载自blog.csdn.net/qq_15437667/article/details/78886894
Why
今日推荐