1

introduction

This series will list some common questions in Go interviews.

Slice loop problem

For loops may be used a lot in our daily coding. In many business scenarios, we need to use for loop processing. But the for loop in golang needs to pay attention to some problems in the use, can you encounter them? First look at the following piece of code:

func testSlice() {
    a := []int64{1,2,3}
    for _, v := range a {
        go func() {
            fmt.Println(v)
        }()
    }
    
    time.Sleep(time.Second)
}

output: 3 3 3

So why is this result output?

In the for loop of golang, the function variables created inside the loop all share the same memory address, and the for loop always uses the same memory to receive the value of the value variable in the loop. No matter how many times it loops, the memory address of value is the same. We can test it:

func testSliceWithAddress() {
    a := []int64{1,2,3}
    for _, v := range a {
        go func() {
            fmt.Println(&v)
        }()

    }

    time.Sleep(time.Second)
}

output:
        0xc0000b2008
        0xc0000b2008
        0xc0000b2008

In line with expectations. If you are more interested, you can print out the assembly of this code, and you can find that the looped v is always operating on the same memory.

Similarly, we will encounter another interesting place in the slice loop. You can see what the output of the code below is.

func testRange3() {
    a := []int64{1,2,3}
    for _, v := range a {
        a = append(a, v)
    }

    fmt.Println(a)
}

The output of this code is: [1 2 3 1 2 3], why? Because golang will copy a before the loop, and then operate on the newly copied a, the number of loops will not increase with append.

Comparison of interface and nil

For example, a null pointer is returned, but not an empty interface

func testInterface() {
    doit := func(arg int) interface{} {
        var result * struct{} = nil
        if arg > 0 {
            result = &struct{}{}
        }

        return result
    }

    if res := doit(-1); res != nil {
        fmt.Println("result:", res)
    }
}

The output result is: result: <nil>, why? Because variables in go have two attributes of type and value, they will be considered equal when they are the same type and value when they are compared. The type of result in the code is a pointer and the value is nil, so there will be such an output.

The variable parameter is an empty interface type

When the variable parameter of the parameter is an empty interface type, you need to pay attention to the problem of parameter expansion when passing in the slice of the empty interface.

func testVolatile() {
    var a = []interface{}{1, 2, 3}

    fmt.Println(a)
    fmt.Println(a...)
}

The output is:

[1 2 3]
1 2 3

The order of map traversal is not fixed

Don't believe the order of the map!

func testMap() {
    m := map[string]string{
        "a": "a",
        "b": "b",
        "c": "c",
    }

    for k, v := range m {
        println(k, v)
    }
}

For the specific reason, you can look at the source code: map.go:mapiterinit, and you will find the code below is used to determine where to start traversing the map. Another reason is that in some specific cases (such as capacity expansion), key relocation and reorganization will occur in the map. The traversal process is to traverse the buckets in order, and at the same time traverse the keys in the buckets in order. After the relocation, the key position has undergone major changes, so the results of traversing the map cannot be in the original order.

func mapiterinit(t *maptype, h *hmap, it *hiter) {
        ......
    // decide where to start
    r := uintptr(fastrand())
        ......
}

Follow us

Readers who are interested in this series of articles are welcome to subscribe to our official account, and pay attention to the blogger not to get lost next time~
image.png


NoSay
449 声望544 粉丝