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-sourced 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's autonomous driving, mini-programs

Tencent's Blue Whale, microservice 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 method

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

Identifier: alphanumeric underscore, 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 based on value
var (
    a string
    b int
    c bool
    d float32
)

Written outside functions as global variables

Local variable declaration 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, counts 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
)

Define 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 left shifted by 10 bits is 1024

Basic Data Types#

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

Corresponding unsigned integers: uint8, uint16, uint32, uint64

uint8 is byte, int16 is short, int64 is long

Special Integers#

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, to exit 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 of 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 based on 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 a pointer array, *[n]T represents an array pointer.

Slices#

The limitation of arrays is their fixed length.

Slices are a variable-length sequence of elements of the same type. They are a layer of encapsulation based on array types. They are very flexible and support automatic resizing.

Slices are a reference type, and their 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 the original array elements will also change 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, same 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 value is 0

Empty Slice Check#

To check if a slice is empty, use len(s) == 0 instead of using 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 a length and capacity of 0 is necessarily nil.

Assignment#

	s1 := make([]int, 3) //[0 0 0]
	s2 := s1             // Assign s1 directly 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 the capacity is sufficient, new elements can be added. When the underlying array cannot accommodate new elements, the slice will automatically resize according to a certain strategy, at which point the underlying array pointed to by the slice will change. The "expansion" operation often occurs during the call to 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 appends elements to the end of the slice and returns that slice.
  2. The capacity of the slice numSlice expands automatically 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, check 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 double 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 increases 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, e.g., *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 based on 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; using the new function returns a pointer of 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, as these three types are reference types.

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

Maps#

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 Maps#

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])
	}
}

Slice of Maps#

	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)
	}

Map with Slice as Value#

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 use the type keyword to define a function type, formatted as follows:

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#

Defining a function inside another function

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

	// Self-executing function: An anonymous function defined and executed immediately
	func(x, y int) {
		fmt.Println(x + y)
	}(10, 20)
}

Closures#

A closure refers to an entity formed by a function and its related reference environment combined. 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 following statement. When the function that defer belongs to is about to return, the deferred statements are executed in reverse order of their definition, meaning 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 executed later 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 find length, for example, 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 append 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 mode 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 the program encounters a panic error, it can be recovered
		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 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 new line
	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 input 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, there are 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 a brand 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

By defining with the type keyword, MyInt is a new type that has the characteristics of int.

Type Aliases#

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

type TypeAlias = Type

The rune and byte types we encountered earlier are type aliases.

type byte = uint8
type rune = int32

Struct Definition#

Use the type and struct keywords to define a struct, formatted 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. The fields of a struct can only be used after instantiation.

Structs themselves are 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 that 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 corresponding zero values of their types. Initialization is the assignment of values during instantiation.

Using key-value pair 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; empty structs do not occupy space.

Constructor Functions#

Implementing constructor functions similar to 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.