Hello everyone, today I will comb out the basic grammar content of Go language and share it with you. Thanks for your advice.

This “Go Language Basic Grammar Content” is divided into three chapters, this paper is the second chapter

  • Golang Foundation (1)
  • Golang Foundation (2)
  • Golang Foundation (3)

Contents of this chapter

  • channel
  • The structure of the body
  • Pointer to the
  • Control statements

channel

introduce

It doesn’t make sense to simply execute functions concurrently. Functions need to exchange data to execute functions concurrently, and channels are the connections between them.

Channel A communication mechanism that allows one Goroutine to send specific values to another goroutine.

A channel is a special type in the Go language. A channel, like a conveyor belt or queue, always follows a First In First Out rule, ensuring the order In which data is sent and received. Each channel is a conduit of a concrete type, which requires the element type to be specified when declaring a channel.

Note: Goroutine is a mechanism unique to the GO language and can be understood as threads in the GO language. With Goroutine, you can use the go keyword

Go concurrency will be shared in a future article. This knowledge point in a simple understanding

use

The channel statement

A channel is a type, a reference type. Syntax format:

varvariablechanThe element typeCopy the code

example

 var ch1 chan int   // Declare a channel for passing integers
 var ch2 chan bool  // Declare a channel for passing booleans
 var ch3 chan []int // Declare a channel to pass int slices
Copy the code

Create a channel

A channel is a reference type, and the null value of a channel type is nil.

The declared channel needs to be initialized using the make function.

make(chanElement type, buffer size)/ / format
Copy the code

example

ch4 := make(chan int)
ch5 := make(chan bool)
ch6 := make(chan []int)
Copy the code

The channel operations

A channel has three operations: send, receive, and close.

Both send and receive use the <- symbol.

send

Sends a value to the channel

ch := make(chan int)
ch <- 100 // Send 100 to ch
Copy the code

receive

Receives values from a channel

x := <- ch // Receive from channel CH and assign x
<- ch // Receive from the CH channel, ignoring the value
Copy the code

Shut down

Call the built-in close function to close the channel

close(ch)
Copy the code

The closed channel has the following characteristics:

  1. Sending values to a closed channel can cause panic
  2. Receiving on a closed channel fetches values until the channel is empty
  3. A receive operation on a closed channel with no value results in a zero value of the corresponding type
  4. Closing a closed channel can cause panic

Note that the channel needs to be closed only if the receiver goroutine is notified that all data has been sent. Channels can be reclaimed by the garbage collection mechanism, which is not the same as closing files. Closing files after an operation is required, but closing channels is not required.

Unbuffered channel

Unbuffered channels are also called blocked channels

An unbuffered channel can send a value only if someone receives it. Just like you live in a community where there is no delivery cabinet or collection point, the Courier must deliver this item to your hand when he calls you. Simply speaking, the unbuffered channel must have reception before it can be sent.

Ch := make(chan int) creates an unbuffered channel

func main(a) {
    ch := make(chan int)
    ch <- 10
    fmt.Println("Sent successfully")}Copy the code

The code above compiles, but it is executed with a deadlock error

fatal error: all goroutines are asleep - deadlock!
Copy the code

One way is to enable a Goroutine to receive values

func recv(c chan int) {
    ret := <-c
    fmt.Println("Received successfully", ret)
}
func main(a) {
    ch := make(chan int)
    go recv(ch) // Enable goroutine to receive values from channels
    ch <- 10
    fmt.Println("Sent successfully")}Copy the code

A send operation on an unbuffered channel blocks until another Goroutine performs a receive operation on that channel, at which point the value can be sent and both Goroutines continue to execute. Conversely, if the receive operation is performed first, the receiver’s Goroutine blocks until another Goroutine sends a value on that channel.

Communicating using an unbuffered channel results in synchronization of the goroutine that sends and receives. Therefore, unbuffered channels are also called synchronous channels.

Buffered channel

A channel is buffered as long as its capacity is greater than zero, which represents the number of elements it can hold. Just like the delivery cabinet in your community only has so many grids. When the grid is full, it will not fit, and it will be blocked. When someone takes away one, the Courier can put another one inside.

Specify channel capacity when initializing a channel using the make function

func main(a) {
    ch := make(chan int.1) // Create a buffered channel of capacity 1
    ch <- 10
    fmt.Println("Sent successfully")}Copy the code

Value case from channel loop

func main(a) {
    ch1 := make(chan int)
    ch2 := make(chan int)
    // Enable goroutine to send numbers from 0 to 100 to CH1
    go func(a) {
        for i := 0; i < 100; i++ {
            ch1 <- i
        }
        close(ch1)
    }()
    // Enable goroutine to receive the value from CH1 and send the square of the value to CH2
    go func(a) {
        for {
            i, ok := <-ch1 // Ok =false after the channel is closed
            if! ok {break
            }
            ch2 <- i * i
        }
        close(ch2)
    }()
    // Receive value prints from CH2 in the main Goroutine
    for i := range ch2 { // The channel closes and exits the for range loop
        fmt.Println(i)
    }
}   
Copy the code

A one-way passage

Sometimes we pass channels as parameters between multiple task functions, and many times we restrict the use of channels in different task functions, such as limiting the channels to only send or receive within a function.

One-way channel case

func counter(out chan<- int) {
    for i := 0; i < 100; i++ {
        out <- i
    }
    close(out)
}
​
func squarer(out chan<- int, in <-chan int) {
    for i := range in {
        out <- i * i
    }
    close(out)
}
func printer(in <-chan int) {
    for i := range in {
        fmt.Println(i)
    }
}
​
func main(a) {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go counter(ch1)
    go squarer(ch2, ch1)
    printer(ch2)
}  
Copy the code

Among them:

  1. chan<- intIs a send only channel, can send but cannot receive;
  2. <-chan intIs a receive – only channel that can receive but cannot send.

It is possible to convert a two-way channel to a one-way channel in function argument passing and any assignment, but not the other way around.

Pointer to the

introduce

Different from the pointer in C/C++, the pointer in Go language cannot be offset and operation, is a safe pointer.

Pointers in Go need to know three concepts: pointer address, pointer type and pointer value.

When we want to change a variable, we can create a pointer variable that points to the address of the variable.

Passing data uses Pointers instead of copying the data.

Pointer manipulation in Go is very simple. You only need to remember two symbols: & (address) and * (value by address).

Each variable has an address at run time that represents its location in memory. The Go language uses the & character in front of a variable to “fetch the address”.

Value types (int, float, bool, string, array, struct) in Go have corresponding pointer types, such as: *int, *int64, *string, etc.

When a pointer is defined without assigning any variables, its value is nil

use

The syntax for fetching a variable pointer is as follows

var v int = 10
ptr := &v
fmt.Printf("v:%d ptr:%p\n", v, ptr)
​
c := *ptr // Pointer value (based on the pointer to memory)
fmt.Printf("c:%d\n", c)
Copy the code

V: the variable used to receive the address. PTR: the variable used to receive the address. The type of PTR is called the pointer type int. * stands for pointer.

The program defines the address of an int variable num and prints it

The address of a is assigned to the pointer P, and the value of A is modified by p

func main(a) {
    var a int
    fmt.Println(&a) // Pointer address
    var p *int
    p = &a  // the same as p := &a
    *p = 20
    fmt.Println(a) / / output 20
}
Copy the code

Null pointer judgment

func main(a) {
    var p *string
    fmt.Printf("P is %v\n", p)
    ifp ! =nil {
        fmt.Println("Not empty")}else {
        fmt.Println("Null")}}Copy the code

The structure of the body

introduce

A structure is an aggregate data type. It is an aggregate entity of zero or more values of any type. Each value is called a member of the structure.

Usually a row corresponds to a structure member whose name comes after the previous type, but adjacent members of the same type can be merged into a row.

If the structure member name begins with an uppercase letter, the member is exported; This is determined by the Go language export rules. A structure may contain both exported and unexported members.

A structure type named S can no longer contain members of type S: the value of an aggregate cannot contain itself. (This restriction also applies to arrays.)

The zero value of a struct type is zero for each member. A value of zero is usually the most reasonable default.

Struct {} is empty if it has no members. It’s zero in size and contains no information, but it can still be valuable sometimes.

use

type Info struct {  // Create a structure
    ID    int
    Name, Hobby  string
}
var info Info // Declare the structure
Copy the code

Members of a structure variable can be accessed through the dot operator

info.Name = "The Gunslinger of Hat Hill."
Copy the code

Takes the address of the member and accesses it through a pointer

name := &info.Name
fmt.Println(*name)
Copy the code

If you want to modify a structure member inside a function, passing in Pointers is necessary; Because in Go, all function arguments are passed in as value copies, function arguments are no longer the original variables used when the function is called.

func UpdateInfo(i *info) {
    i.Hobby = "Travel"
}
Copy the code

Creates and initializes a structure variable using a pointer and returns the address of the structure

pp := &Point{1.2}
Copy the code

It is equivalent to the following statement

pp := new(Point)
*pp = Point{1.2}
Copy the code

Note: Structures are also comparable, and both structures will be able to use == or! = operator for comparison. The equality comparison operator == compares each member of two structures.

Control statements

If judgment

Single conditional judgment

if condition {
    // do something
}
Copy the code

Multiple conditional judgment

if condition {
    
} else if condition {
    // do something
} else {
    // do something
}
Copy the code

If a single condition is followed by a statement and then the condition is judged

ifstatement; condition{//do something
}
Copy the code

Multiple conditions with statement judgment

if num := 78; num <=50{
    fmt.Println("Number is less then 50")}else if num >= 51 && num <= 100{
    fmt.Println("The number is between 51 and1 100")}else{
    fmt.Println("The number is greater than 100")}Copy the code

The for loop

There is only one loop in go, the for loop.

The first syntax format

forLoop variable initialization; Cyclic conditions; Loop variable iteration {// Loop operation (statement)
}
​
for j := 1; j <= 10; j++ {
    // Loop through the statement
}
Copy the code

Second syntax format

forCyclic judgment condition {// Loop through the statement
}
​
j := 1
for j <= 10 {
    j++
}
Copy the code

The third syntax format

for {
    // A loop execution statement, which is an infinite loop, usually needs to be used with a break statement
}
Copy the code

Traversal syntax format for-range

var names []string{"xiaoming"."xiaohong"."xiaojun"}
for _, name := range names {
    fmt.Println(name)
}
Copy the code

usegotoStatement breaks out of the for loop

func main(a) {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
        if i == 5 {
            goto end
        }
    }
    end:
        fmt.Println("end")}Copy the code

The output

0
1
2
3
4
5
end
Copy the code

switch

The first syntax format

func main(a) {
    num := 1
    switch num {
        case 1:
          fmt.Println("num=1")
        case 2:
          fmt.Println("num=2")
        case 3:
          fmt.Println("num=3")
        default:
          fmt.Println("No match")}}Copy the code

Second syntax format

func main(a) {
    num := 1
    switch {
        case num == 1:
          fmt.Println("num=1")
        case num == 2:
          fmt.Println("num=2")
        case num == 3:
          fmt.Println("num=3")
        default:
          fmt.Println("No match")}}Copy the code

The third syntax format

func main(a) {
    switch num := 1; {
        case num == 1:
          fmt.Println("num=1")
        case num == 2:
          fmt.Println("num=2")
        case num == 3:
          fmt.Println("num=3")
        default:
          fmt.Println("No match")}}Copy the code

The fourth syntax format

Use the keyword fallthrough. By default, in switch, each case has a hidden break. If we want to remove the hidden break, we can use fallthrough to replace it

package main
​
import (
    "fmt"
)
​
func main(a) {
    a := 2
    switch a {
    case 1:
        fmt.Println("a=1")
    case 2:
        fmt.Println("a=2")
        fallthrough
    case 3:
        fmt.Println("a=3")
        case 4:
        fmt.Println("a=4")
    default:
        fmt.Println("default")}}Copy the code

The output

a=2
a=3
Copy the code

select

The SELECT statement is used to handle channel-related I/O operations:

  1. Each case must be a communication;
  2. All channel expressions and sent expressions are evaluated;
  3. Any channel can run, it executes, others are ignored;
  4. Multiple cases can be run, one of which can be randomly selected.
  5. Do not run, if there is default, execute default, block if there is none, until some communication can run without reevaluating the expression;
  6. A select executes the code in the case at most once, always checking the case, and enclosing the for loop;
  7. A break in case only exits the current select and has nothing to do with the for loop;

Perform case usage randomly

package main
​
import (
    "fmt"
    "time"
)
​
func main(a) {
    ch1 := make(chan int)
    ch2 := make(chan int)
​
    go func(a) {
        time.Sleep(time.Second)
        ch1 <- 1} ()go func(a) {
        time.Sleep(time.Second)
        ch2 <- 2
    }()
​
    time.Sleep(2 * time.Second)
    select {
    case i := <-ch1:
        fmt.Println("ch1 receive", i)
    case i := <-ch2:
        fmt.Println("ch2 receive", i)
    default:
        fmt.Println("no i/o opeartion")}}Copy the code

Ch1 receive: 1; ch2 receive: 2; ch1 receive: 1; ch2 receive: 2; ch1 receive: 1; ch2 receive: 2

Set the receive channel timeout usage

func main(a) {
    ch1 := make(chan int)
    select {
    case i := <-ch1:
        fmt.Println(i)
    case <-time.After(5 * time.Second):
        fmt.Println("ch receive timeout")}}Copy the code

Check if the channel is full of usage

func main(a) {
    ch1 := make(chan int.5)
    ch1 <- 1
    ch1 <- 2
    ch1 <- 3
    ch1 <- 4
    ch1 <- 5
​
    select {
    case ch1 <- 6:
        fmt.Println("send 6 to ch1")
    default:
        fmt.Println("channel is full")}}Copy the code

Technical articles continue to be updated, please pay more attention to it