Go 语言从新手到大神:每个人都会踩的五十个坑(转)

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

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

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

“{”不能单独放在一行

级别:新手入门级

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

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

出错代码:

package main

import "fmt"

func main()
{ //error, can‘t have the opening brace on a separate line
    fmt.Println("hello there!")
}

错误信息:

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

修正代码:

package main

import "fmt"

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

未使用已定义的变量

级别:新手入门级

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

对于未声明变量的调用同样会导致编译失败。和C语言一样,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

    func(unused string) {
        fmt.Println("Unused arg. No compile error")
    }("what?")
}

错误信息:

/tmp/sandbox473116179/main.go:6: one declared and not used /tmp/sandbox473116179/main.go:7: two declared and not used /tmp/sandbox473116179/main.go:8: three declared and not used

修正代码:

package main

import "fmt"

func main() {
    var one int
    _ = one

    two := 2
    fmt.Println(two)

    var three int
    three = 3
    one = three

    var four int
    four = four
}

当然,你也可以考虑删除那些没有使用的变量。

未使用的包

级别:新手入门级

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

如果真的确认要引入变量但是不使用的话,我们可以用“”标识符坐标记,避免编译失败。“”标识符表示为了得到这些包的副作用而引入这些包。

出错代码:

package main

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

func main() {
}

错误信息:

/tmp/sandbox627475386/main.go:4: imported and not used: "fmt"
/tmp/sandbox627475386/main.go:5: imported and not used: "log"
/tmp/sandbox627475386/main.go:6: imported and not used: "time"

修正代码

package main

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

var _ = log.Println

func main() {
    _ = time.Now
}

只能在函数内部使用简短的变量声明

级别:新手入门级 出错代码:

package main

myvar := 1 //error

func main() {
}

错误信息:

/tmp/sandbox265716165/main.go:3: non-declaration statement outside function body

修正代码:

package main

var myvar = 1

func main() {
}

无法使用精简的赋值语句对变量重新赋值

级别:新手入门级

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

并且,重定义的变量必须写在同一个代码块。

错误信息:

package main

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

错误信息:

/tmp/sandbox706333626/main.go:5: no new variables on left side of :=

修正代码:

package main

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

    one,two = two,one
}

隐式变量(作用域)

级别:新手入门级

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

package main

import "fmt"

func main() {
    x := 1
    fmt.Println(x)     //打印 1
    {
        fmt.Println(x) //打印 1
        x := 2
        fmt.Println(x) //打印 2
    }
    fmt.Println(x)     //打印 1 ( 不是 2)
}

甚至对于有经验的开发者来说,这也是个不注意就会掉进去的深坑。

除非特别指定,否则无法使用 nil 对变量赋值

级别:新手入门级

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

错误信息:

package main

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

    _ = x
}

错误信息:

/tmp/sandbox188239583/main.go:4: use of untyped nil

修正代码:

package main

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

    _ = x
}

Slice 和 Map 的 nil 值

级别:新手入门级

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

修正代码:

package main

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

错误信息:

package main

func main() {
    var m map[string]int
    m["one"] = 1 //error

}

Map 定长

级别:新手入门级

创建 Map 的时候可以指定 Map 的长度,但是在运行时是无法使用 cap() 功能重新指定 Map 的大小,Map 是定长的。

错误信息:

package main

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

错误信息:

/tmp/sandbox326543983/main.go:5: invalid argument m (type map[string]int) for cap

字符串无法为 nil

级别:新手入门级

所有的开发者都可能踩的坑,在 C 语言中是可以 char *String=NULL,但是 Go 语言中就无法赋值为 nil。

错误信息:

package main

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

    if x == nil { //error
        x = "default"
    }
}

Compile Errors:

/tmp/sandbox630560459/main.go:4: cannot use nil as type string in assignment /tmp/sandbox630560459/main.go:6: invalid operation: x == nil (mismatched types string and nil)

修正代码:

package main

func main() {
    var x string //defaults to "" (zero value)

    if x == "" {
        x = "default"
    }
}

参数中的数组

Array Function Arguments

级别:新手入门级 对于 C 和 C++ 开发者来说,数组就是指针。给函数传递数组就是传递内存地址,对数组的修改就是对原地址数据的修改。但是 Go 语言中,传递的数组不是内存地址,而是原数组的拷贝,所以是无法通过传递数组的方法去修改原地址的数据的。

package main

import "fmt"

func main() {
    x := [3]int{1,2,3}

    func(arr [3]int) {
        arr[0] = 7
        fmt.Println(arr) //prints [7 2 3]
    }(x)

    fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
}

如果需要修改原数组的数据,需要使用数组指针(array pointer)。

package main

import "fmt"

func main() {
    x := [3]int{1,2,3}

    func(arr *[3]int) {
        (*arr)[0] = 7
        fmt.Println(arr) //prints &[7 2 3]
    }(&x)

    fmt.Println(x) //prints [7 2 3]
}

或者可以使用 Slice,

package main

import "fmt"

func main() {
    x := []int{1,2,3}

    func(arr []int) {
        arr[0] = 7
        fmt.Println(arr) //prints [7 2 3]
    }(x)

    fmt.Println(x) //prints [7 2 3]
}

使用 Slice 和 Array 的 range 会导致预料外的结果

级别:新手入门级

如果你对别的语言中的 for in 和 foreach 熟悉的话,那么 Go 中的 range 使用方法完全不一样。因为每次的 range 都会返回两个值,第一个值是在 Slice 和 Array 中的编号,第二个是对应的数据。

出错代码:

package main

import "fmt"

func main() {
    x := []string{"a","b","c"}

    for v := range x {
        fmt.Println(v) //prints 0, 1, 2
    }
}

修正代码:

package main

import "fmt"

func main() {
    x := []string{"a","b","c"}

    for _, v := range x {
        fmt.Println(v) //prints a, b, c
    }
}
时间: 2025-01-23 14:50:13

Go 语言从新手到大神:每个人都会踩的五十个坑(转)的相关文章

jquery-图片轮播(新手请大神指教一下)

这是我刚学jquery写的,感觉效果不是很好. #scrollPics{ height: 330px; width: 980px; margin-bottom: 10px; overflow: hidden; position:relative;}.slider ul{ padding: 0px;}.slider ul li{ float: left; list-style: none; width: 980px;}.num{ position:absolute; right:5px; bott

关于xml的出错。新手求大神解救。

============问题描述============ ============解决方案1============ 上面的看看是什么错误信息,下面的打成了中文的引号了吧. ============解决方案2============ Project clean一下 ============解决方案3============ 建议将汉字都写到strings.xml下面去,比如 <resources>      <string name="colum">信息列数&l

新手入门大数据,这有一条最完整的学习路径

本文的目的是希望给所有大数据初学者规划一条比较清晰的学习路线,帮助它们开启大数据学习之旅.鉴于大数据领域内的技术绚丽繁复,每位大数据初学者都应该根据自己的实际情况制定专属的学习路径. 要说当下IT行业什么最火?ABC无出其右.所谓ABC者,AI + Big Data + Cloud也,即人工智能.大数据和云计算(云平台).每个领域目前都有行业领袖在引领前行,今天我们来讨论下大数据这个方向. 新手入门大数据,这有一条最完整的学习路径大数据概念 角色 以我的愚见,当下大数据行业有两类角色: 大数据工

大神营销值爆表,X7将成战斗机?

昨日,大神@祝芳浩博微博发布了题为<智能手机市场"七大恨">战斗檄文,并放言大神将做出一款真正超越iPhone的旗舰手机,为国产智能手机正名! @祝芳浩在微博长文中列举的国内外智能手机的各种乱象.文中不仅吐槽了苹果.三星的对手机市场利润的攫取,也吐槽了苹果产品设计和质量上存在的诟病,指出了三星空有技术与体系优势却创新乏力的现实,还指出了小米追求销量成心魔,榜样正在沦陷的现状,华为空有体量,却只知道复制小米,缠斗魅族的无奈...... <智能手机市场"七大恨&

理解C#语言中的类型转换----初学者的理解,请大神指教

一下都是在视频教学中学到后的理解,如果说错了请大神指教 C#语言中的类型转换,就是将某个数据要转换成另一个类型的数据. c#语言中的数据类型主要有: char类型(字符类型): string类型(字符串类型): int类型(整数类型): double类型(小数类型): 类型转换主要分为三种: 1:任意类型转换为string类型: 转换代码书写格式为:待转换的数据.Tostring(): a,这里的待转换的数据指的是需要转换的数据或变量.后面的Tostring():是固定书写. 转换完成后的返回类

【真&#183;新手初篇】菜鸟们都戳进来看看(欢迎大神指导)

作为一只菜鸟,我希望这篇文能帮助到同是新人的你 ———————————————————————————————————————————————————— 首先我必须感谢@CoffeeDeveloper,他的文章给了我行动的勇气,读读他置顶的文章相信对你也会有帮助 如果你想学习HTML,我向你推荐一个网站(部分收费课程),里面有一整套学习教程(虽说那老师的口音...). 视频中提及的一款软件intellji IDEA ,如果有需要的戳这里 .那啥,找提取码就把鼠标移到链接上. 缺少学习和参考的材料

新人迷茫,求大神指导

毕业快半年,工作还算稳定.但是作为新手有点迷茫,求各路大神指导. 1.工作上使用的是c#/net,web开发,想学点新东西但是因为工作上用不到怕学完就忘了,而且也怀疑学习这些新东西的价值,比如python.之前实习学过mybaties,因为换工作没用,已经忘得差不多了.才有了这种迷茫想法.也有不学东西就等着做码农的想法,怎么破. 2.程序猿有必要掌握多门语言吗?工作上大部分只会用到一种语言,多个技能多口饭,但是这个学习成本值得付出吗? 3.我不是计算机专业,对编程原理,算法,软件工程的概念都不清

无法运行的贪吃蛇游戏代码,求大神帮忙修改!

#include <iostream> #include <windows.h> #include <stdlib.h> #include <conio.h> #include <time.h> //使用当前时间做种子 enum dir {up,down,left,right}; //枚举类型enum dir //围墙 class Fence { public: void initFence(); void OutputF(); public:

听justjavac大神live前端的入门与进阶小笔记

代码规范 代码强壮,调试代码 少用变量,多用常量 少用for循环,why循环,多用函数式, 不要直接去使用框架 刷题 提高编程思维 用js去做c语音的问题 阅读别人代码,去看别人的代码 a+b>c =>a>c-b可以防止用?溢出 函数要短小,代码不要超过一屏 维护性看设计模式 编程的目的 为了解决问题而编程 训练思维方式:编程计算根号7,二分查找 学习奇特语言,来扩展自己的思路 没有系统的js,不是为了编程而编程,获取某些知识 javascript高级编程 如何阅读一本书(书名) 不够系