10 questions about go basic grammar

1.Recover execution timing

None, recover must be run in the defer function. recover captures exceptions when called at the grandparent level, and is invalid when called directly.

func main() {
    
    
    recover()
    panic(1)
}

Direct defer calls are also invalid.

func main() {
    
    
    defer recover()
    panic(1)
}

Multi-level nesting is still invalid when defer is called.

func main() {
    
    
    defer func() {
    
    
        func() {
    
     recover() }()
    }()
    panic(1)
}

It must be called directly in the defer function to be effective.

func main() {
    
    
    defer func() {
    
    
        recover()
    }()
    panic(1)
}

2. How to deal with the problem of closure incorrectly referencing the same variable

Generate a 在这里插入代码片local . Without the i := i line, the same variable would be printed.

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        i := i
        defer func() {
    
    
            println(i)
        }()
    }
}

Or pass in i through function parameters.

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        defer func(i int) {
    
    
            println(i)
        }(i)
    }
}

3. What happens when the defer statement is executed inside the loop?

defer can only be executed when the function exits. Executing defer in for will cause delayed release of resources.

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        func() {
    
    
            f, err := os.Open("/path/to/file")
            if err != nil {
    
    
                log.Fatal(err)
            }
            defer f.Close()
        }()
    }
}

func is a local function, there will be no problem executing defer in the local function.

4. Name a measure to avoid Goroutine leaks

Memory leaks can be avoided through the context package.

func main() {
    
    
    ctx, cancel := context.WithCancel(context.Background())

    ch := func(ctx context.Context) <-chan int {
    
    
        ch := make(chan int)
        go func() {
    
    
            for i := 0; ; i++ {
    
    
                select {
    
    
                case <- ctx.Done():
                    return
                case ch <- i:
                }
            }
        } ()
        return ch
    }(ctx)

    for v := range ch {
    
    
        fmt.Println(v)
        if v == 5 {
    
    
            cancel()
            break
        }
    }
}

When the for loop below stops fetching data, it uses the cancel function to stop another coroutine from writing data. If the for below has stopped reading data and the above for loop is still writing, it will cause a memory leak.

5. How to get out of the for select loop

Usually in a for loop, break can be used to break out of the loop, but note that in the Go language, break cannot break out of the loop when for select is used.

func testSelectFor2(chExit chan bool){
    
    
 EXIT:
    for  {
    
    
        select {
    
    
        case v, ok := <-chExit:
            if !ok {
    
    
                fmt.Println("close channel 2", v)
                break EXIT//goto EXIT2
            }

            fmt.Println("ch2 val =", v)
        }
    }

    //EXIT2:
    fmt.Println("exit testSelectFor2")
}

6. How to find in slices

The sort.searchXXX method is used in go to search for the specified method in the sorted slice, but its return is the position subscript to be inserted when the corresponding search element does not exist (the element is inserted before the returned subscript).

The purpose can be achieved by encapsulating the following functions.

func IsExist(s []string, t string) (int, bool) {
    
    
    iIndex := sort.SearchStrings(s, t)
    bExist := iIndex!=len(s) && s[iIndex]==t

    return iIndex, bExist
}

7. How to initialize a structure with nested structures

The philosophy of go is that combination is better than inheritance. Combination can be completed by using struct nesting. The properties of the embedded structure are just like the properties of the outer structure and can be called directly.
Note that when initializing the outer structure, you must specify the structure initialization of the embedded structure name. As shown below, an error is reported in the s1 method, and the s2 method is correct.

type stPeople struct {
    
    
    Gender bool
    Name string
}

type stStudent struct {
    
    
    stPeople
    Class int
}

//尝试4 嵌套结构的初始化表达式
//var s1 = stStudent{false, "JimWen", 3}
var s2 = stStudent{
    
    stPeople{
    
    false, "JimWen"}, 3}
fmt.Println(s2.Gender, s2.Name, s2.Class)

8. The difference between slices and arrays

An array is a fixed-length sequence of zero or more elements of the same data type. The length of the array is part of the array type, so [3]int and [4]int are two different array types. The size of the array needs to be specified. If not specified, the size will be automatically calculated based on initialization and cannot be changed; arrays are passed by value. Array is a built-in type, which is a collection of data of the same type. It is a value type, and the element value is accessed through a subscript index starting from 0. The length is fixed after initialization and cannot be modified.
When passed as a parameter to a method, a copy of the array will be copied instead of referencing the same pointer. The length of an array is also part of its type, and its length is obtained through the built-in function len(array). Array definition:

var array [10]int

var array =[5]int{
    
    1,2,3,4,5}

A slice represents a variable-length sequence of elements of the same type. A slice is a lightweight data structure with three properties: pointer, length and capacity. Slices do not need to be specified in size; slices are passed by address; slices can be initialized through an array or through the built-in function make(). When initializing, len=cap. When appending elements, if the capacity cap is insufficient, the capacity will be expanded by 2 times of len. Slice definition:

var slice []type = make([]type, len)

9.The difference between new and make

The function of new is to initialize a pointer to type (*T). The new function is a built-in function, function definition: func new(Type) Type. Use the new function to allocate space. What is passed to the new function is a type, not a value. The return value is a pointer to this newly allocated zero value.
The function of make is to initialize and return the reference (T) for slice, map or chan. The make function is a built-in function, function definition: func make(Type, size IntegerType) Type; the first parameter is a type, the second parameter is the length; the return value is a type.
The make(T, args) function has a different purpose than new(T).
It is only used to create Slice, Map and Channel, and the return type is an initialized (not zero-valued) instance of T (not T ).

10.What are the differences and usages of Printf(), Sprintf() and Fprintf() functions?

They all output formatted strings, but the output targets are different.
Printf() outputs the format string to standard output (usually the screen, which can be redirected). Printf() is associated with the standard output file (stdout), but Fprintf does not have this limitation.
Sprintf() outputs the format string into the specified string, so the parameter is one more char* than printf. That is the target string address.
Fprintf() outputs the format string to the specified file device, so the parameter is one more file pointer FILE* than printf. Mainly used for file operations. Fprintf() formats output to a stream, usually to a file.

Guess you like

Origin blog.csdn.net/m0_73728511/article/details/133471634