Go language learning record (4)

1. Go language type conversion

Type conversion is used to convert a variable of one data type to another type of variable. The basic format of Go language type conversion is as follows:

type_name(expression)

type_name is the type and expression is the expression.

In the following example, the integer type is converted to a floating point type, the result is calculated, and the result is assigned to a floating point type variable:

package main

import "fmt"

func main() {
   var sum int = 17
   var count int = 5
   var mean float32
   
   mean = float32(sum)/float32(count)
   fmt.Printf("mean 的值为: %f\n",mean)
}

The output of the above example execution is:

The value of mean is: 3.400000

The type conversion method of Go language is similar to the forced type conversion of C++.

2. Go language interface

The Go language provides another data type, the interface, which defines all common methods together. As long as any other type implements these methods, it implements this interface.

Here is a little more knowledge about the method :

There are both functions and methods in Go language. A method is a function with a receiver, which can be a value or a pointer of a named or struct type. All methods of a given type belong to that type's method set. The syntax format is as follows:

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函数体*/
}

The following defines a struct type and a method of that type:

package main

import (
   "fmt"  
)

/* 定义结构体 */
type Circle struct {
  radius float64
}

func main() {
  var c1 Circle
  c1.radius = 10.00
  fmt.Println("圆的面积 = ", c1.getArea())
}

//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
  //c.radius 即为 Circle 类型对象中的属性
  return 3.14 * c.radius * c.radius
}

The above code execution result is:

圆的面积 =  314

In Go language, methods and functions are almost the same. What I understand is the difference between object-oriented and process-oriented. Methods are object-oriented functions, and process-oriented functions should not be called methods. The same is true for C++. Since Go language also has functions The two concepts of method and method show that this language is both object-oriented and process-oriented.

Interface usage format:

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}

example

package main

import (
    "fmt"
)

type Phone interface {
    call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}

func main() {
    var phone Phone

    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

An interface is a data type formed by encapsulating the definitions of multiple common methods . As for the implementation of the interface, it needs to be written according to the instantiated object.

3. Go error handling

The Go language provides a very simple error handling mechanism through the built-in error interface.

The error type is an interface type, here is its definition:

type error interface {
    Error() string
}

We can generate error messages by implementing the error interface type in coding.

Functions usually return an error message in the final return value. Use errors.New to return an error message:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

In the following example, we pass a negative number when calling Sqrt, and then get a non-nil error object, compare this object with nil, and the result is true, so fmt.Println(fmt package handles error will call the Error method) is called to output an error, please see the sample code of the call below:

result, err:= Sqrt(-1)

if err != nil {
   fmt.Println(err)
}

Example:

package main

import (
    "fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
    dividee int
    divider int
}

// 实现 `error` 接口
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
            dData := DivideError{
                    dividee: varDividee,
                    divider: varDivider,
            }
            errorMsg = dData.Error()
            return
    } else {
            return varDividee / varDivider, ""
    }

}

func main() {

    // 正常情况
    if result, errorMsg := Divide(100, 10); errorMsg == "" {
            fmt.Println("100/10 = ", result)
    }
    // 当除数为零的时候会返回错误信息
    if _, errorMsg := Divide(100, 0); errorMsg != "" {
            fmt.Println("errorMsg is: ", errorMsg)
    }

}

Execute the above program, the output is:

100/10 =  10
errorMsg is:  
    Cannot proceed, the divider is zero.
    dividee: 100
    divider: 0

The Go language has a built-in error interface, which can be used after implementing the error type in actual use.

4. Go concurrency

The Go language supports concurrency, we only need to start the goroutine with the go keyword.

Goroutines are lightweight threads, and the scheduling of goroutines is managed by the Golang runtime.

Goroutine syntax format:

go 函数名( 参数列表 )

For example:

go f(x, y, z)

Start a new goroutine:

f(x, y, z)

Go allows the use of the go statement to start a new runtime thread, a goroutine, to execute a function in a different, newly created goroutine. All goroutines in the same program share the same address space.

package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}

func main() {
        go say("world")
        say("hello")
}

Execute the above code, you will see that the output hello and world are in no fixed order. Because they are two goroutines executing:

world
hello
hello
world
world
hello
hello
world
world
hello

channel

A channel is a data structure used to transfer data.

Channels can be used to synchronize execution and communication between two goroutines by passing a value of a specified type. Operators  <- are used to specify the direction of the channel, send or receive. If no direction is specified, it is a bidirectional channel.

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

Declaring a channel is very simple, we can use the chan keyword, the channel must be created before use:

ch := make(chan int)

Note : By default, channels are unbuffered. When the sender sends data, there must be corresponding data received by the receiver.

The following example computes the sum of a number through two goroutines, and after the goroutine finishes computing, it computes the sum of the two results:

package main

import "fmt"

func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
                sum += v
        }
        c <- sum // 把 sum 发送到通道 c
}

func main() {
        s := []int{7, 2, 8, -9, 4, 0}

        c := make(chan int)
        go sum(s[:len(s)/2], c)
        go sum(s[len(s)/2:], c)
        x, y := <-c, <-c // 从通道 c 中接收

        fmt.Println(x, y, x+y)
}

The output is:

-5 17 12

channel buffer

The channel can set the buffer, and specify the buffer size through the second parameter of make:

ch := make(chan int, 100)

The channel with a buffer allows the sending of data at the sender and the acquisition of data at the receiver to be in an asynchronous state, which means that the data sent by the sender can be placed in the buffer and wait for the receiver to get the data instead of immediately requiring the receiver to get the data .

However, since the size of the buffer is limited, there must still be a receiving end to receive the data, otherwise, once the buffer is full, the data sending end will no longer be able to send data.

Note : If the channel is not buffered, the sender will block until the receiver has received a value from the channel. If the channel is buffered, the sender will block until the sent value is copied into the buffer; if the buffer is full, it means waiting until a receiver gets a value. The receiver blocks until there is a value to receive.

package main

import "fmt"

func main() {
    // 这里我们定义了一个可以存储整数类型的带缓冲通道
        // 缓冲区大小为2
        ch := make(chan int, 2)

        // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
        // 而不用立刻需要去同步读取数据
        ch <- 1
        ch <- 2

        // 获取这两个数据
        fmt.Println(<-ch)
        fmt.Println(<-ch)
}

The execution output is:

1
2

Go traverses channels and closes channels

Go uses the range keyword to traverse the read data, similar to arrays or slices. The format is as follows:

v, ok := <-ch

If the channel does not receive data, ok is false, then the channel can be closed using the close() function.

package main

import (
        "fmt"
)

func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ {
                c <- x
                x, y = y, x+y
        }
        close(c)
}

func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
        // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
        // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
        // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
        // 会结束,从而在接收第 11 个数据的时候就阻塞了。
        for i := range c {
                fmt.Println(i)
        }
}

The execution output is:

0
1
1
2
3
5
8
13
21
34

Go language concurrency is implemented through channels, and the operation is more convenient than C++ threads. Range can traverse the elements of the channel, but special attention should be paid to the fact that range traversal will not stop when the channel is not closed.

5. Summary

The type conversion of Go language is similar to the forced type conversion of C++; the interface is also similar to that of general object-oriented programming languages; error handling needs to implement the built-in error interface, which is relatively simple; concurrency is implemented through the unique data type channel and goroutine, which seems to be still Very convenient.

After learning this, the learning content of Go language is basically over, and I will continue to add new discoveries in the actual programming process~

Guess you like

Origin blog.csdn.net/qq_43824745/article/details/126257992