Three mistakes Golang beginners are prone to make

Source: Internet
Author: User
from Golang small white to become a Golang engineer nearly two months, I want to share the novice in the development of common mistakes, are I personally stepped on the pit. Some of these errors can lead to failure to compile, this error is easy to find, and some errors will not be thrown at compile time, even at run time will not be panic, if the lack of knowledge, scratching the scalp can not understand where the bug is.

1. Add data to nil map, nil slice

Consider whether this code is wrong, and then run it again:

package mainfunc main() {    var m map[string]string    m["name"] = "zzy"}

Without an accident, this code will result in a panic:

panic: assignment to entry in nil map

This is because the code simply declares the type of map, but does not create the underlying array for the map, at which point the map does not actually exist in memory, that is, nil map, which can be verified by running the following code:

package mainimport "fmt"func main() {    var m map[string]string    if m == nil {        fmt.Println("this map is a nil map")    }}

So to use the map smoothly, be sure to create it using the built-in function make function:

m := make(map[string]string)

It is also possible to use the literal method, with the same effect as make:

m := map[string]string{}

Similarly, adding data directly to the nil slice is not allowed, because the underlying array of slice is not initialized by the Make function, only the slice type is declared, and the underlying array does not exist:

package mainfunc main() {    var s []int    s[0] = 1}

The code above will produce a panic runtime error:index out of range , and the correct approach should be to use the Make function or the literal:

package mainfunc main() {    //第二个参数是slice的len,make slice时必须提供,还可以传入第三个参数作为cap      s := make([]int, 1)     s[0] = 1}

It may be found that the use of the Append function for nil Slice is also valid without make:

package mainimport "fmt"func main() {    var s []int    s = append(s, 1)    fmt.Println(s) // s => [1]}

That's because slice itself is like a struct, it has a Len attribute, is the current length, there is a cap property, is the length of the underlying array, the APPEND function will determine the Len and cap of the incoming slice, and if Len is going to be larger than the CAP, Call the Make function to generate a larger new array and copy the data from the original underlying array (the above are my guess, without verification, interested students can go to challenge the source code), the process is similar:

package mainimport "fmt"func main() {    var s []int //len(s)和cap(s)都是0    s = append(s, 1)    fmt.Println(s) // s => [1]}func append(s []int, arg int) []int {    newLen := len(s) + 1    var newS []int    if newLen > cap(s) {        //创建新的slice,其底层数组扩容为原先的两倍多        newS = make([]int, newLen, newLen*2)        copy(newS, s)    } else {        newS = s[:newLen] //直接在原数组上切一下就行    }    newS[len(s)] = arg    return newS}

Nil map, nil slice wrong use is not very scary, after all, compile time can be found, the following to say a mistake is very pit dad, accidentally in strokes, difficult to troubleshoot.

2. Misuse: = Assignment causes variable override

Look at this code first, and guess what it will print:

package mainimport (    "errors"    "fmt")func main() {    i := 2    if i > 1 {        i, err := doDivision(i, 2)        if err != nil {            panic(err)        }        fmt.Println(i)    }    fmt.Println(i)}func doDivision(x, y int) (int, error) {    if y == 0 {        return 0, errors.New("input is invalid")    }    return x / y, nil}

I guess someone would think it was:

11

The actual execution of the result is:

12

Why is it so!?
This is because the scope of the variables in the Golang range is small to each lexical block (the incomprehensible classmate can simply be the {} part of the parcel) is a separate scope, everyone knows that the internal declaration of each scope will mask the external declaration of the same name, and each if statement is a lexical block, that is to say , if you accidentally assign a variable in a statement instead of if := = an if outside statement, a new local variable is generated and only if valid after the assignment statement in the statement, and an external variable of the same name is masked. There will be no change due to the logic behind this assignment statement!
At the language level this may not be a mistake, but if you misuse it in your actual work, the bugs will be very secretive. such as the code in the example, because it err is not declared before, so use the := assignment (figure easy, less write var err error ), and then neither in the compile times wrong, nor in the run times wrong, it will let you baffled its solution, think their logic clearly go right, why the final result is always wrong , until you 1.1 debugging, only to find yourself accidentally wrote one more .
Because of this has been the pit several times, every time I have been looking at the logic of a loophole, and finally found that it was = written := , alas, it is tears.

3. Passing a value as a reference

Difference between value type data and reference type data I'm sure everyone here can get it out, otherwise you don't have to look down because you don't understand.
In Golang, array and struct are value types, and, slice map chan are reference types, so when we write code, basically do not use array , but slice instead of it, for struct the use of pointers as far as possible, This avoids the time and space consumption of copying data when a variable is passed, and avoids the situation where the original data cannot be modified.
If this is unclear, the result may be a defective code and, more serious, a bug.
Consider this piece of code and run it:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p1 := person{name: "zzy", age: 100}    p2 := person{name: "dj", age: 99}    p3 := person{name: "px", age: 20}    people := []person{p1, p2, p3}    whoIsDead(people)    for _, p := range people {        if p.isDead {            fmt.Println("who is dead?", p.name)        }    }}func whoIsDead(people []person) {    for _, p := range people {        if p.age < 50 {            p.isDead = true        }    }}

I believe a lot of people see the problem at a glance, but there are certainly people do not know the for range mechanism of grammar, I ramble about: Golang in the for range syntax is very convenient, can easily traverse array , slice , and map other structures, but it has a characteristic, is to copy the currently traversed element into the internal variable, in the whoIsDead function for range , each of which will be people person copied to the p variable, similar to the operation:

p := person

As mentioned above, struct is a value type, so in the assignment to p the process, you actually need to regenerate a copy of the person data, for for range internal use, do not believe to try:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p1 := person{name: "zzy", age: 100}    p2 := p1    p1.name = "changed"    fmt.Println(p2.name)}

So p.isDead = true this operation actually changes the newly generated p data, not the people Central Plains person , where a bug is generated.
In the case of the for range internal only need to read the data without modification, it does not matter how to write, at most, the code is not perfect, and need to modify the data, it is best to pass the struct pointer:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p1 := &person{name: "zzy", age: 100}    p2 := &person{name: "dj", age: 99}    p3 := &person{name: "px", age: 20}    people := []*person{p1, p2, p3}    whoIsDead(people)    for _, p := range people {        if p.isDead {            fmt.Println("who is dead?", p.name)        }    }}func whoIsDead(people []*person) {    for _, p := range people {        if p.age < 50 {            p.isDead = true        }    }}

Run it:

who is dead? px

Everything is OK, very good code.
There is another way to use the index access people person , change the whoIsDead function, also can achieve the same purpose:

func whoIsDead(people []person) {    for i := 0; i < len(people); i++ {        if people[i].age < 50 {            people[i].isDead = true        }    }}

Well, for range part of it, let's talk about map the transfer and modification of the value in the structure.
This code changes the previous to the people []person map structure, we think there is a mistake, if wrong, where wrong:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p1 := person{name: "zzy", age: 100}    p2 := person{name: "dj", age: 99}    p3 := person{name: "px", age: 20}    people := map[string]person{        p1.name: p1,        p2.name: p2,        p3.name: p3,    }    whoIsDead(people)    if p3.isDead {        fmt.Println("who is dead?", p3.name)    }}func whoIsDead(people map[string]person) {    for name, _ := range people {        if people[name].age < 50 {            people[name].isDead = true        }    }}

go run, Error:

cannot assign to struct field people[name].isDead in map

This error is a bit crazy, I think a lot of people can not understand it. I answer, map uses array to store data, and there is no capacity limit, and with the increase of the map element, you need to create a larger array To store the data, the previous address is invalid because the data is copied to a new, larger array , so the elements in the map are not addressable and cannot be modified. This error means that the element in map is not allowed to be modified.
Even if the element in the map does not have the above limitation, this code is still wrong, think about it, why? The answer has been said before.
So, how to change the correct, the old routine, still use the pointer:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p1 := &person{name: "zzy", age: 100}    p2 := &person{name: "dj", age: 99}    p3 := &person{name: "px", age: 20}    people := map[string]*person{        p1.name: p1,        p2.name: p2,        p3.name: p3,    }    whoIsDead(people)    if p3.isDead {        fmt.Println("who is dead?", p3.name)    }}func whoIsDead(people map[string]*person) {    for name, _ := range people {        if people[name].age < 50 {            people[name].isDead = true        }    }}

In addition, interface{} when you attempt to modify a property directly in an assertion struct rather than through a pointer modification:

package maintype person struct {    name   string    age    byte    isDead bool}func main() {    p := person{name: "zzy", age: 100}    isDead(p)}func isDead(p interface{}) {    if p.(person).age < 101 {        p.(person).isDead = true    }}

will report a compilation error directly:

cannot assign to p.(person).isDead

Even though the code is wrong to compile, always remember that struct it is a value type, use the pointer to manipulate it, and the correct approach is:

package mainimport "fmt"type person struct {    name   string    age    byte    isDead bool}func main() {    p := &person{name: "zzy", age: 100}    isDead(p)    fmt.Println(p)}func isDead(p interface{}) {    if p.(*person).age < 101 {        p.(*person).isDead = true    }}

Finally, it must be said that the Golang in the hands of the home travel, promotion of the necessary knowledge of salary increase, hope that the students master proficiency.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.