golang陷阱

Go Gotchas

Posted by BY on June 11, 2019

前言

新的一年,好好学习。

正文

问题来源

本问题来自Go Gotchas

1.直接给空的map赋值

错误代码

var m map[string]float64
m["pi"] = 3.1416

执行结果

panic: assignment to entry in nil map

解答

需要提前分配空间

m := make(map[string]float64) //或者
var m map[string]float64
m = make(map[string]float64)
//make为一个map分配空间时,可以指定其容量,这个容量并非是map容量的上界
m := make(map[string]float64, 100) //map底层是hashmap,容量可以减少冲突

2.非法的内存地址或空指针引用

和C、C++、Java一样,不用详细讲解

3.多值返回单值接收

和Python类似,不用详细讲解

4.数组作为函数参数内容并不会改

这点和C、C++、Java均不一样

错误代码

func Foo(a [2]int) {
    a[0] = 8
}
func main() {
    a := [2]int{1, 2}
    Foo(a)         // Try to change a[0].
    fmt.Println(a) // Output: [1 2]
}

解答

数组在Go语言中是一些值,当将其传递给一个函数时,数组将会被拷贝,如果需求是需要改变其内容,可以选择用切片slice。(这点和大多数语言不同)

5.作用域问题

同一变量名在不同作用域下,并且他们有公共的作用域空间,作用域最小那个将会隐藏其他变量的值。这个与其他语言一样。

6.意外的换行

错误代码

func main() {
    fruit := []string{
        "apple",
        "banana",
        "cherry"
    }
    fmt.Println(fruit)
}

执行结果

../main.go:5:11: syntax error: unexpected newline, expecting comma or }

解答

在多行的切片、数组、map中每行结尾都需要有个逗号。

func main() {
    fruit := []string{
        "apple",
        "banana",
        "cherry", // comma added
    }
    fmt.Println(fruit) // "[apple banana cherry]"
}

6.字符串不可变

错误代码

s := "hello"
s[0] = 'H'
fmt.Println(s)

执行结果

../main.go:3:7: cannot assign to s[0]

解答

Go语言中的字符串是不可变的,其行为像只读字节切片(有少量的额外权利)。如果需要对其内容进行改变,可以使用:

buf := []rune("hello")
buf[0] = 'H'
s := string(buf)
fmt.Println(s)  // "Hello"

6.字符串相加与字符相加

代码

fmt.Println("H" + "i")
fmt.Println('H' + 'i')

执行结果

Hi
177

7.TrimRight函数问题

代码

fmt.Println(strings.TrimRight("ABBA", "BA")) // Output: ""

解答

Trim,TrimLeft和TrimRight函数的第二参数并非切除的字符串,而是需要切除的集合(与C++一致,Java仅仅删除它能够去除从编码’\u0000′ 至 ‘\u0020′ 的所有字符,不能指定去除的集合)。
如果想将字符串尾部的指定字符串删除,可以用strings.TrimSuffix方法。

复制时为开辟空间

代码

var src, dst []int
src = []int{1, 2, 3}
copy(dst, src) // Copy elements to dst from src.
fmt.Println("dst:", dst) //Output: dst: []

解答

dst = make([]int, len(src)) //在copy之前开辟空间
//或者将copy语句改为
dst = append(dst, src...)

### 8.非法的++操作

错误代码

i := 0
fmt.Println(++i)
fmt.Println(i++)

执行结果

main.go:9:14: syntax error: unexpected ++, expecting expression
main.go:10:15: syntax error: unexpected ++, expecting comma or )

解答

首先、Go语言只有后置++和–操作,没有前置++和–操作;其次、Go语言不允许将后置++或者–作为表达式是使用。
为什么++和–是语句而不是表达式,为何是后置而非前置
### 9.以0开头的数字 以0开头的数字在Go语言中为8进制数。(与C、Java语言相同)

10.range更改实体

错误代码

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

解答

range循环将元素的值拷贝一份到临时变量中,这个和Java中的foreach语句类似。 改为:

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

11.range和数组

错误代码

var a [2]int
for _, x := range a {
    fmt.Println("x =", x)
    a[1] = 8
}
fmt.Println("a =", a)

执行结果

x = 0
x = 0        <- Why isn't this 8?
a = [0 8]

解答

在range表达式在开始执行前,会拷贝一份数组的副本用于生成迭代值。
为了避免拷贝,可以这样做:

var a [2]int
for _, x := range a[:] {
    fmt.Println("x =", x)
    a[1] = 8
}
fmt.Println("a =", a)

12.Json序列化未成功

type Person struct {
    name string
    age  int
}

p := Person{"Alice", 22}
jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData)) //Output:{}

解答

Go语言中需要序列化的字段需要大写。如果需要Json串中字段名确定,可以使用json:tag方式。

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

p := Person{"Alice", 22}

jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData))
Output:{"name":"Alice","age":22}

13.空和空不同

错误代码

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

func main() {
    err := Foo()
    fmt.Println(err)        // <nil>
    fmt.Println(err == nil) // false
}

解答

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

或者

func Foo() (err error) {
    // …
    return // err is unassigned and has zero value [nil, nil]
}

func main() {
    err := Foo()
    fmt.Println(err)        // <nil>
    fmt.Println(err == nil) // true
}

总结:

勤思考。

结语

不管怎么样好好加油。