KANIKIG

KANIKIG

just for fun | 兴趣使然 Ph.D. in Engineering|❤️ #NFT $ETH| [Twitter](https://twitter.com/kanikig2)|[Github](https://github.com/KANIKIG)|[Telegram channel](https://t.me/kanikigtech)

Golang Basic Notes

image

Preface#

Introduction to Go#

  • Open source by Google
  • Compiled language
  • The C language of the 21st century

In 2005, multi-core processors emerged, while other languages were born in the single-core era. Go was designed with multi-core concurrency in mind.

Features:

  • Simple syntax (only 25 keywords, simpler than Python, built-in formatting, easy to read)
  • High development efficiency
  • Good execution performance (close to Java)

Development:

Baidu autonomous driving, mini-programs

Tencent Blue Whale, microservices framework

Zhihu was initially written in Python, but later could not handle the load and was restructured in Go, saving 80% of resources.

Course Introduction#

Li Wenzhou's Blog

Repository

8 weeks of basics

3 practical projects

Go Project Structure#

Individual developers

image-20220113164729380

Popular methods

image-20220113165000771

Helloworld#

go build

Compiles to exe on Windows, executable file on macOS

go install

Install is equivalent to building and then moving to bin

go run

Runs the script

Supports cross-platform cross-compilation

// wincmd SET, macos export
export CGO_ENABLED=0	// Disable CGO
export GOOS=linux	// Set target platform linux, windows, darwin
export GOARCH=amd64// Target processor architecture is amd64
go build
export CGO_ENABLED=0 GOOS=linux GOARCH=amd64
go build

Variables and Constants#

Statements cannot be written outside functions

Identifiers: alphanumeric underscores, cannot start with a number

Keywords and reserved words are not recommended for variable names

Variables#

Initialization#

Numbers default to 0, strings default to empty, booleans default to false, slices, functions, pointers default to nil.

var variableName type = expression
var name string = "Q1mi"
var age int = 18
var name, age = "Q1mi", 20	// Type inferred from value
var (
    a string
    b int
    c bool
    d float32
)

Declared outside functions as global variables

Local variables declared inside functions can be abbreviated as

	n := 10
	m := 200
	fmt.Println(m, n)

Note: In Golang, non-global variable declarations must be used, otherwise compilation will fail!

fmt.Print()
fmt.Printf()
fmt.Println() // New line

Automatically formats when saved

Naming Rules#

var studentName string

Golang uses camelCase naming

Anonymous Variables#

Receive with a short underscore, do not occupy namespace, do not allocate memory

x, _ = foo()
_, y = foo()

Constants#

const pi = 3.14

iota#

Constant counter, increments with each new line of constant declaration, note it is one line

const (
  n1 = iota //0
  n2	//1
  n3	//2
  n4	//3
)
const (
  n1 = iota //0
  n2	//1
  _	//2  but discarded
  n3	//3
)

Defining Magnitudes#

const (
  _ = iota
  KB = 1 << (10 * iota)
  MB = 1 << (10 * iota)
  GB = 1 << (10 * iota)
  TB = 1 << (10 * iota)
  PB = 1 << (10 * iota)
)

<< Left shift operator, binary 1 shifted left 10 bits is 1024

Basic Data Types#

Integer types are divided into the following two categories: by length: int8, int16, int32, int64

Corresponding unsigned integer types: uint8, uint16, uint32, uint64

uint8 is byte, int16 is short, int64 is long

Special Integer Types#

uint int will be determined by the system as 32 or 64

uintptr pointer, stores memory addresses

Number Systems#

Golang cannot directly define binary numbers, octal and hexadecimal are both possible

	// Decimal
	var a int = 10
	fmt.Printf("%d \n", a)  // 10
	fmt.Printf("%b \n", a)  // 1010  Placeholder %b indicates binary
 
	// Octal  Starts with 0
	var b int = 077
	fmt.Printf("%o \n", b)  // 77
 
	// Hexadecimal  Starts with 0x
	var c int = 0xff
	fmt.Printf("%x \n", c)  // ff
	fmt.Printf("%X \n", c)  // FF
	fmt.Printf("%T \n", c)  // Output type
	fmt.Printf("%v \n", c)  // Output variable value, any type

Floating Point Numbers#

In golang, decimals default to float64

math.MaxFloat64 // maximum value of float64

Boolean#

Defaults to false, no conversion allowed

Strings#

Only double quotes are allowed, single quotes are for characters

EscapeMeaning
\rReturn to the start
\nNew line (same column)
\tTab
// Escape path in Windows
s := "D:\\Documents\\A"

// Backticks output as is, multi-line string
s := `
asda
		asd
`
s := "D:\Documents\A"
len(str)
ss := s1 + s2
ret := strings.Split(s3, "\\")
ret = strings.Contains(s3, "abcd")
ret = strings.HasPrefix(s3, "abcd")
ret = strings.HasSuffix(s3, "abcd")
ret = strings.Index(s3, "c")
ret = strings.LastIndex(s3, "c")
ret = strings.Join(a, b)

English characters are byte, other languages such as Chinese characters are rune, which is actually int32, occupying 3 bytes

String traversal

for _, char := range str {
  fmt.Printf("%c", char)
}

Strings cannot be modified directly, they must be converted to other types for processing

s3 := []rune(s2)	// Slice
s3[0] = 'e'	// Modify
s4 := string(s3)

Control Flow#

if#

if expression1 {
    branch1
} else if expression2 {
    branch2
} else{
    branch3
}
// Local variable score only effective in if, reducing memory usage
	if score := 65; score >= 90 {
		fmt.Println("A")
	} else if score > 75 {
		fmt.Println("B")
	} else {
		fmt.Println("C")
	}

for#

Golang only has for

for initialization; condition; ending {
    loop body statements
}
for i := 0; i < 10; i++ {
		fmt.Println(i)
}

Initialization and ending statements can be omitted, equivalent to while

i := 0
	for i < 10 {
		fmt.Println(i)
		i++
	}

Infinite loop

for {
    loop body statements
}

Exit the loop forcibly through break, goto, return, panic statements

Traversal#

for range traverses arrays, slices, strings, maps, and channels

for i,v := range s{
  fmt.Println(i, v)
}
  1. Arrays, slices, strings return index and value.
  2. Maps return key and value.
  3. Channels only return values in the channel.

switch#

finger := 3
	switch finger {
	case 1:
		fmt.Println("Thumb")
    fallthrough
	case 2:
		fmt.Println("Index Finger")
	case 3:
		fmt.Println("Middle Finger")
	case 4:
		fmt.Println("Ring Finger")
	case 5:
		fmt.Println("Little Finger")
	default:
		fmt.Println("Invalid input!")
	}

The fallthrough syntax can execute the next case of the satisfied condition, designed for compatibility with case in C language

switch n := 7; n {
	case 1, 3, 5, 7, 9:
		fmt.Println("Odd")
	case 2, 4, 6, 8:
		fmt.Println("Even")
	default:
		fmt.Println(n)
	}

goto#

The goto statement performs an unconditional jump between code via labels. The goto statement can help quickly exit loops and avoid repeated exits. Using goto in Go can simplify some code implementations. For example, when exiting a double nested for loop

var breakFlag bool
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// Set exit label
				breakFlag = true
				break
			}
			fmt.Printf("%v-%v\n", i, j)
		}
		// Outer for loop judgment
		if breakFlag {
			break
		}
	}

Simplified to

for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// Set exit label
				goto breakTag
			}
			fmt.Printf("%v-%v\n", i, j)
		}
	}
	return
	// Label
breakTag:
	fmt.Println("End for loop")

Operators#

++ (increment) and -- (decrement) are separate statements in Go, not operators.

// Logical operations
&&
||
!

// Bitwise operations
&
|
^
<<
>>

// Assignment
+=
-=
<<=

Arrays#

Initialization#

Arrays are determined at declaration, members can be modified during use, but the size of the array cannot change

var a [3]int

var a [3]int
var b [4]int
a = b // Cannot do this, as a and b are different types

Arrays can be accessed via indices, starting from 0, the last element index is: len-1, accessing out of bounds (index outside the valid range) triggers an out-of-bounds panic

	var testArray [3]int	// Array will initialize to zero value of int type
	var numArray = [3]int{1, 2}	// Initialize using specified initial values
	var cityArray = [3]string{"Beijing", "Shanghai", "Shenzhen"} // Initialize using specified initial values
	var numArray = [...]int{1, 2} // Infer array length from values
	var cityArray = [...]string{"Beijing", "Shanghai", "Shenzhen"}

	a := [...]int{1: 1, 3: 5}	// Initialize with specified index
	fmt.Println(a)                  // [0 1 0 5]
for index, value := range a {
		fmt.Println(index, value)
	}

Multi-dimensional Arrays#

a := [3][2]string{
		{"Beijing", "Shanghai"},
		{"Guangzhou", "Shenzhen"},
		{"Chengdu", "Chongqing"},
	}

Only the first layer of multi-dimensional arrays can use ... to let the compiler infer the array length

Arrays are value types, assignment and parameter passing will copy the entire array. Therefore, changing the value of the copy will not change the original value.

  1. Arrays support “==“ and “!=” operators, as memory is always initialized.
  2. [n]*T represents an array of pointers, *[n]T represents a pointer to an array.

Slices#

The limitation of arrays is that their length is fixed.

A slice is a variable-length sequence of elements of the same type. It is a layer of encapsulation based on the array type. It is very flexible and supports automatic expansion.

A slice is a reference type, and its internal structure contains address, length, and capacity. Slices are generally used for quick operations on a block of data.

Initialization#

	var a = []string              // Declare a string slice
	var b = []int{}             // Declare an integer slice and initialize
	var c = []bool{false, true} // Declare a boolean slice and initialize
	var d = []bool{false, true} // Declare a boolean slice and initialize

Slices are not empty when pointing to values.

a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
s3 := a1[0:4] // Left inclusive, right exclusive, slice from index 0 to 3

len(s3) // 4 Slice length
cap(s3)	// 7 Capacity = length from slice point to end of original array

a[2:]  // Equivalent to a[2:len(a)]
a[:3]  // Equivalent to a[0:3]
a[:]   // Equivalent to a[0:len(a)]

Modifying original array elements also changes the slice, as it is a reference type.

a[low : high : max]
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5] // t:[2 3] len(t):2 cap(t):4

Constructing with a simple slice expression a[low: high] results in a slice of the same type, length, and elements. Additionally, it sets the capacity of the resulting slice to max-low. In a complete slice expression, only the first index value (low) can be omitted; it defaults to 0.

make()#

Dynamically create a slice

make([]T, size, cap)

a := make([]int, 2, 10) // Initialized to 0

Empty Slice Check#

To check if a slice is empty, use len(s) == 0 rather than s == nil.

Slices cannot be compared; we cannot use the == operator to determine if two slices contain all equal elements. The only valid comparison operation for slices is with nil. A nil slice has no underlying array, and both its length and capacity are 0. However, we cannot say that a slice with length and capacity both 0 is necessarily nil.

Assignment#

	s1 := make([]int, 3) //[0 0 0]
	s2 := s1             // Directly assign s1 to s2, s1 and s2 share an underlying array
	s2[0] = 100
	fmt.Println(s1) //[100 0 0]
	fmt.Println(s2) //[100 0 0]

append()#

	var s []int
	s = append(s, 1)        // [1]
	s = append(s, 2, 3, 4)  // [1 2 3 4]
	s2 := []int{5, 6, 7}  
	s = append(s, s2...)    // [1 2 3 4 5 6 7]

A zero-value slice declared with var can be used directly in the append() function without initialization

var s []int
s = append(s, 1, 2, 3)

Each slice points to an underlying array, and if that array has enough capacity, new elements can be added. When the underlying array cannot accommodate new elements, the slice will automatically expand according to a certain strategy, at which point the underlying array pointed to by the slice will change. The "expansion" operation often occurs when calling the append() function, so we usually need to receive the return value of the append function with the original variable.

func main() {
	// append() adds elements and expands the slice
	var numSlice []int
	for i := 0; i < 10; i++ {
		numSlice = append(numSlice, i)
		fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
	}
}

Output

[0]  len:1  cap:1  ptr:0xc0000a8000
[0 1]  len:2  cap:2  ptr:0xc0000a8040
[0 1 2]  len:3  cap:4  ptr:0xc0000b2020
[0 1 2 3]  len:4  cap:4  ptr:0xc0000b2020
[0 1 2 3 4]  len:5  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5]  len:6  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6]  len:7  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6 7]  len:8  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6 7 8]  len:9  cap:16  ptr:0xc0000b8000
[0 1 2 3 4 5 6 7 8 9]  len:10  cap:16  ptr:0xc0000b8000

From the above results, we can see:

  1. The append() function adds elements to the end of the slice and returns that slice.
  2. The capacity of the slice numSlice expands according to the rules of 1, 2, 4, 8, 16, doubling each time.

$GOROOT/src/runtime/slice.go source code:

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
	newcap = cap
} else {
	if old.len < 1024 {
		newcap = doublecap
	} else {
		// Check 0 < newcap to detect overflow
		// and prevent an infinite loop.
		for 0 < newcap && newcap < cap {
			newcap += newcap / 4
		}
		// Set newcap to the requested cap when
		// the newcap calculation overflowed.
		if newcap <= 0 {
			newcap = cap
		}
	}
}
  • First, it checks if the newly requested capacity (cap) is greater than twice the old capacity (old.cap), then the final capacity (newcap) is the newly requested capacity (cap).
  • Otherwise, if the old slice's length is less than 1024, the final capacity (newcap) is twice the old capacity (old.cap), i.e., (newcap=doublecap).
  • Otherwise, if the old slice length is greater than or equal to 1024, the final capacity (newcap) starts from the old capacity (old.cap) and loops to increase by 1/4 of the original until the final capacity (newcap) is greater than or equal to the newly requested capacity (cap), i.e., (newcap >= cap).
  • If the final capacity (cap) calculation overflows, then the final capacity (cap) is the newly requested capacity (cap).

Chinese strings are 3*2^n

copy()#

Slices are reference types, so a and b actually point to the same memory address. Modifying b will also change the value of a.

The built-in copy() function in Go can quickly copy data from one slice to another slice space.

	a := []int{1, 2, 3, 4, 5}
	c := make([]int, 5, 5)
	copy(c, a)     // Use copy() function to copy elements from slice a to slice c
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1 2 3 4 5]
	c[0] = 1000
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1000 2 3 4 5]

Deleting Elements#

	a = append(a[:index], a[index+1:]...)
	a := []int{30, 31, 32, 33, 34, 35, 36, 37}
	// To delete the element at index 2
	a = append(a[:2], a[3:]...)
	fmt.Println(a) //[30 31 33 34 35 36 37]

	// The underlying array length remains unchanged, elements shift left, right filled by the rightmost element

Sorting slices

sort.Ints(a[:])

Pointers#

ptr := &v // v's type is T, output pointer type *T, such as *string *int

	a := 10
	b := &a
	fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
	fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
	fmt.Println(&b)                    // 0xc00000e018
	c := *b // Pointer dereferencing (getting value from memory via pointer)
	fmt.Printf("type of c:%T\n", c)
	fmt.Printf("value of c:%v\n", c)

& and * are complementary

func modify1(x int) {
	x = 100
}

func modify2(x *int) {
	*x = 100
}

func main() {
	a := 10
	modify1(a)
	fmt.Println(a) // 10
	modify2(&a)
	fmt.Println(a) // 100
}

new and make#

The new function is not commonly used; it returns a pointer to a type, and the value corresponding to that pointer is the zero value of that type.

	a := new(int)
	b := new(bool)
	fmt.Printf("%T\n", a) // *int
	fmt.Printf("%T\n", b) // *bool
	fmt.Println(*a)       // 0
	fmt.Println(*b)       // false

Make is also used for memory allocation, but unlike new, it is only used for creating memory for slices, maps, and channels, and it returns the type itself rather than its pointer type, because these three types are reference types.

	var b map[string]int
	b = make(map[string]int, 10)
	b["Shahe Nazha"] = 100
	fmt.Println(b)

Map#

The mapping container provided by Go is map, which is implemented using a hash table, similar to Python's dictionary.

A map is an unordered data structure based on key-value, and maps in Go are reference types that must be initialized before use.

The default initial value of a map type variable is nil, and memory must be allocated using the make() function.

	map[KeyType]ValueType

	scoreMap := make(map[string]int, 8) // Must initialize to avoid dynamic expansion!
	scoreMap["Zhang San"] = 90
	scoreMap["Xiao Ming"] = 100
	fmt.Println(scoreMap)
	fmt.Println(scoreMap["Xiao Ming"])
	fmt.Printf("type of a:%T\n", scoreMap)

	userInfo := map[string]string{
		"username": "Shahe Little Prince",
		"password": "123456",
	}

Check if Key Exists#

value, ok := map[key] // ok returns a bool indicating whether the key exists

	v, ok := scoreMap["Zhang San"]
	if ok {
		fmt.Println(v)
	} else {
		fmt.Println("No such person found")
	}

Traversing Map#

for k, v := range scoreMap {
		fmt.Println(k, v)
}

for k := range scoreMap {
		fmt.Println(k)
}

for _, v := range scoreMap {
		fmt.Println(v)
}

Note: The order of elements when traversing a map is unrelated to the order in which key-value pairs were added

Deleting Key-Value Pairs#

delete(map, key)

Traverse in Specified Order#

func main() {
	rand.Seed(time.Now().UnixNano()) // Initialize random seed

	var scoreMap = make(map[string]int, 200)

	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("stu%02d", i) // Generate strings starting with stu
		value := rand.Intn(100)          // Generate random integers from 0 to 99
		scoreMap[key] = value
	}
	// Extract all keys from the map into a slice
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	// Sort the slice
	sort.Strings(keys)
	// Traverse the map according to the sorted keys
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
}

Elements as Map Types in Slices#

	var mapSlice = make([]map[string]string, 3) // Slice initialization, each element is a map
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// Initialize map elements in the slice
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "Little Prince"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "Shahe"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}

Values as Slice Types in Maps#

func main() {
	var sliceMap = make(map[string][]string, 3)
	fmt.Println(sliceMap)
	fmt.Println("after init")
	key := "China"
	value, ok := sliceMap[key]
	if !ok {
		value = make([]string, 0, 2)
	}
	value = append(value, "Beijing", "Shanghai")
	sliceMap[key] = value
	fmt.Println(sliceMap)
}

Functions#

func functionName(parameter type) returnType {
    function body
}

func intSum(x int, y int) int {
	return x + y
}

Parameter Type Simplification#

func intSum(x, y int) int {
	return x + y
}

Variable Parameters#

func intSum2(x ...int) int {
	fmt.Println(x) // x is a slice
	sum := 0
	for _, v := range x {
		sum = sum + v
	}
	return sum
}

Return Values#

// Named returns
func calc(x, y int) (sum, sub int) {
	sum = x + y
	sub = x - y
	return
}
// Slice
func someFunc(x string) []int {
	if x == "" {
		return nil // No need to return []int{}
	}
	...
}

If a local variable and a global variable have the same name, the local variable takes precedence.

Function Types and Variables#

We can define a function type using the type keyword, in the following format:

type calculation func(int, int) int

The above statement defines a calculation type, which is a function type that takes two int parameters and returns an int return value.

func main() {
	var c calculation               // Declare a variable c of type calculation
	c = add                         // Assign add to c
	fmt.Printf("type of c:%T\n", c) // type of c:main.calculation
	fmt.Println(c(1, 2))            // Call c like calling add

	f := add                        // Assign function add to variable f
	fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int
	fmt.Println(f(10, 20))          // Call f like calling add
}

Functions as Parameters and Return Values#

func add(x, y int) int {
	return x + y
}
func calc(x, y int, op func(int, int) int) int {
	return op(x, y)
}
func main() {
	ret2 := calc(10, 20, add)
	fmt.Println(ret2) //30
}
func do(s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("Unrecognized operator")
		return nil, err
	}
}

Anonymous Functions#

Define a function inside another function

func main() {
	// Save anonymous function to variable
	add := func(x, y int) {
		fmt.Println(x + y)
	}
	add(10, 20) // Call anonymous function via variable

	// Self-executing function: define an anonymous function and execute it immediately
	func(x, y int) {
		fmt.Println(x + y)
	}(10, 20)
}

Closures#

A closure refers to an entity formed by a function and its associated reference environment. In simple terms, closure = function + reference environment

func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder()
	fmt.Println(f(10)) //10
	fmt.Println(f(20)) //30
	fmt.Println(f(30)) //60

	f1 := adder()
	fmt.Println(f1(40)) //40
	fmt.Println(f1(50)) //90
}

defer#

The defer statement delays the execution of the statement that follows it. When the function to which defer belongs is about to return, the delayed statements are executed in reverse order of their definition, meaning that the first deferred statement is executed last, and the last deferred statement is executed first.

func main() {
	fmt.Println("start")
	defer fmt.Println(1)
	defer fmt.Println(2)
	defer fmt.Println(3)
	fmt.Println("end")
}

/*
start
end
3
2
1
*/

image-20220120184447325

// Interview question: When registering a function to be delayed with defer, all parameters of that function must have their values determined
func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

func main() {
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20
}

/*
A 1 2 3 //defer calc("AA", 1, 3)
B 10 2 12 //defer calc("BB", 10, 12)
BB 10 12 22
AA 1 3 4
*/

Built-in Functions#

Built-in FunctionDescription
closeMainly used to close channels
lenUsed to calculate length, such as for string, array, slice, map, channel
newUsed to allocate memory, mainly for value types like int, struct. Returns a pointer
makeUsed to allocate memory, mainly for reference types like chan, map, slice
appendUsed to add elements to arrays, slices
panic and recoverUsed for error handling

Currently (Go1.12), Go does not have an exception mechanism, but uses the panic/recover model to handle errors. panic can be triggered anywhere, but recover is only effective in functions called by defer.

func funcA() {
	fmt.Println("func A")
}

func funcB() {
	defer func() {
		err := recover()
		// If a panic error occurs in the program, it can be recovered through recover
		if err != nil {
			fmt.Println("recover in B")
		}
	}()
	panic("panic in B")
}

func funcC() {
	fmt.Println("func C")
}
func main() {
	funcA()
	funcB()
	funcC()
}
  1. recover() must be used with defer.
  2. defer must be defined before statements that may trigger panic.

fmt Standard Library#

The fmt package implements formatted I/O similar to C language's printf and scanf. It is mainly divided into two parts: outputting content and obtaining input content.

Print#

func main() {
	fmt.Print("Print this message in the terminal.") // No newline
	name := "Shahe Little Prince"
	fmt.Printf("I am: %s\n", name)
	fmt.Println("Print a separate line in the terminal")
}

FPrint#

The Fprint series of functions will output content to a variable of type io.Writer interface, which we usually use to write content to files.

// Write content to standard output
fmt.Fprintln(os.Stdout, "Write content to standard output")
fileObj, err := os.OpenFile("./xx.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
	fmt.Println("Error opening file, err:", err)
	return
}
name := "Shahe Little Prince"
// Write content to the opened file handle
fmt.Fprintf(fileObj, "Write information to the file: %s", name)

Any type that satisfies the io.Writer interface supports writing

Sprint#

The Sprint series of functions generates and returns a string from the passed data.

s3 := fmt.Sprintln("Shahe Little Prince")

Errorf#

e := errors.New("Original error e")
w := fmt.Errorf("Wrapped an error %w", e)

Scan#

fmt.Scan(&name, &age, &married)
fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married)
fmt.Scanln(&name, &age, &married)

Also includes Fscan, Sscan

bufio.NewReader#

func bufioDemo() {
	reader := bufio.NewReader(os.Stdin) // Generate read object from standard input
	fmt.Print("Please enter content:")
	text, _ := reader.ReadString('\n') // Read until newline, spaces are also read
	text = strings.TrimSpace(text)
	fmt.Printf("%#v\n", text)
}

Structs#

Go does not have the concept of "classes" and does not support "class" inheritance and other object-oriented concepts. Go achieves higher extensibility and flexibility through struct embedding combined with interfaces.

Custom Types#

A custom type defines an entirely new type. We can define it based on built-in basic types or through struct definitions.

// Define MyInt as an int type
type MyInt int

Through the definition with the type keyword, MyInt is a new type that has the characteristics of int.

Type Aliases#

Type aliasing states: TypeAlias is just an alias for Type; essentially, TypeAlias and Type are the same type.

type TypeAlias = Type

We have previously seen that rune and byte are type aliases.

type byte = uint8
type rune = int32

Struct Definition#

Use the type and struct keywords to define a struct, the specific code format is as follows:

type TypeName struct {
    FieldName FieldType
    FieldName FieldType

}

type person struct {
	name string
	city string
	age  int8
}

type person1 struct {
	name, city string
	age        int8
}

Where:

  • TypeName: Identifies the name of the custom struct, which cannot be duplicated within the same package.
  • FieldName: Represents the field names of the struct. Field names within a struct must be unique.
  • FieldType: Represents the specific type of the struct field.

Instantiation#

Memory is only allocated when a struct is instantiated. Fields of a struct can only be used after instantiation.

A struct itself is also a type, and we can use the var keyword to declare a struct type just like we do with built-in types.

var structInstance structType

Basic instantiation

type person struct {
	name string
	city string
	age  int8
}

func main() {
	var p1 person
	p1.name = "Shahe Nazha"
	p1.city = "Beijing"
	p1.age = 18
	fmt.Printf("p1=%v\n", p1)  //p1={Shahe Nazha Beijing 18}
	fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"Shahe Nazha", city:"Beijing", age:18}
}

Anonymous structs are used for temporary data structures.

func main() {
    var user struct{Name string; Age int}
    user.Name = "Little Prince"
    user.Age = 18
    fmt.Printf("%#v\n", user)
}

Pointer type structs, using new to allocate addresses.

var p2 = new(person)
// Using & to take the address of the struct is equivalent to instantiating the struct type with new
p3 := &person{}


fmt.Printf("%T\n", p2)     //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}

// Supports directly using . to access struct members on struct pointers
p2.name = "Little Prince"
p2.age = 28
p2.city = "Shanghai"
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"Little Prince", city:"Shanghai", age:28}

Initialization#

Structs that are not initialized have their member variables set to the zero value corresponding to their type. Initialization is the assignment of values during instantiation.

Using key-value pairs for initialization

p5 := person{
  name: "Little Prince",
	city: "Beijing",
	age:  18,
}

Initializing a struct pointer

p6 := &person{
	name: "Little Prince",
	city: "Beijing",
	age:  18,
}

Using list initialization

p8 := &person{
	"Shahe Nazha",
	"Beijing",
	28,
}

Memory Layout#

Structs occupy a contiguous block of memory, and empty structs do not occupy space.

Constructor Functions#

Implementing constructor functions similar to those in other object-oriented languages, Go is oriented towards interface programming.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.