头图

Hello everyone, today I will sort out the usage of Go language functions and share them with you. Please advise, thank you.

This "Go Language Function Usage" is divided into three chapters, and this article is the third chapter.

Contents of this chapter

  • init function
  • method

init function

introduce

For example, in some scenarios, we need to initialize some variables or logic code in advance. In this case, we can use a special init initialization function to simplify the initialization work, each file can contain one or more init initialization functions.

 func init() {} // init()函数语法

The init initialization function behaves like a normal function except that it cannot be called or referenced. The init initialization functions in each file are automatically called when the program starts executing in the order in which they are declared.

The init function is executed before the main function

Note: Each package is initialized in the order of import declarations under the premise of resolving dependencies, and each package will only be initialized once. Therefore, if a p package imports the q package, then it can be considered that the q package must have been initialized when the p package is initialized.

Characteristics of the init function

  • The init function is a function used to initialize the package before the program is executed, such as initializing the variables in the package, etc.
  • The init function has no input parameters and return value
  • Each package can have multiple init functions
  • Each source file of a package can also have multiple init functions
  • The execution order of multiple init functions in the same package is not clearly defined in the go language (description)
  • The init functions of different packages determine the execution order of the initialization functions according to the dependencies of package imports
  • The init function cannot be called by other functions, but is automatically called before the main function is executed

initialization process

  1. Initialize the imported packages (the order is not in the import order (from top to bottom), the runtime needs to resolve the package dependencies, and the packages without dependencies are initialized first);
  2. Initialize variables in the package scope (not in the order of "top to bottom, left to right", runtime resolves variable dependencies, and variables without dependencies are initialized first);
  3. Execute the init function of the package;
The runtime is the infrastructure required for the go language to run, and it is also the core feature of go. This content will be shared with you in the follow-up "Features of Go Basics" chapter.

use

Case: init initialization sequence

 package main

import "fmt"

var Num int = Call() // 全局变量声明

func init() { // 初始化函数
    fmt.Println("init()")
}

func Call() int {
    fmt.Println("Call()")
    return 1
}

func main() {
    fmt.Println("main()")
}

output

 Call()
init()
main()

Conclusion, the initialization process: Num变量初始化 -> init() -> main()

Case: init initialization sequence of different source code of the same package

First, create three files. The main.go code contains global variables, the definition of the init initialization function, and the main function entry; the a.go and b.go code files only contain the definitions of global variables and the init initialization function.

main.go file

 package main

import (
    "fmt"
)

var _ int = m()

func init() {
   fmt.Println("init in main.go")
}

func m() int {
   fmt.Println("call m() in main.go")
   return 1
}

func main() {
   fmt.Println("main()")
}

a.go file

 package main

import "fmt"

var _ int = a()

func init() {
   fmt.Println("init in a.go")
}

func a() int {
   fmt.Println("call a() in a.go")
   return 1
}

b.go file

 package main

import "fmt"

var _ int = b()

func init() {
   fmt.Println("init in b.go")
}

func b() int {
   fmt.Println("call b() in b.go")
   return 1
}
Because both a.go and b.go belong to the main package, but there is no main function entry in the two files. When executing, you need to use go run main.go a.go b.go to execute in this form, and the runtime will load and initialize all files.

output

 call m() in main.go
call a() in a.go
call b() in b.go
init in main.go
init in a.go
init in b.go
main()

In conclusion, golang has not made an official description of the execution order of the init functions of different source files in the same package. This block loading process is sorted by go run file.

Case: Multiple init function initialization sequence

 package main

import "fmt"

func init() {
   fmt.Println("init 1")
}

func init() {
   fmt.Println("init 2")
}

func main() {
   fmt.Println("main")
}

output

 init 1
init 2
main

Conclusion: The init function is special and can be defined multiple times in a package.

method

introduce

The method in Golang is implemented by binding an object instance and implicitly using the instance as the first argument (receiver).

Definition description

  • Methods can only be defined for named types in the current package;
  • Parameter receiver can be named arbitrarily, if it is not used in the method, the parameter name can be omitted;
  • Parameter receiver type can be T or *T, and the base type T cannot be an interface or a pointer;
  • Method overloading is not supported, receiver is only part of the parameter signature;
  • Available instances value or pointer call all methods, the compiler automatically converts

A method is a function that contains a receiver, which can be a value or a pointer of a named or struct type.

method definition

 func (recevier type) methodName(参数列表) (返回值列表) {} // 参数和返回值可以省略

use

define a struct type and a method of that type

 package main

import "fmt"

// 结构体
type Info struct {
    Name  string
    Desc string
}

// 方法
func (u Info) Output() {
    fmt.Printf("%v: %v \n", u.Name, u.Desc)
}

func main() {
    // 值类型调用方法
    u1 := Info{"帽儿山的枪手", "分享技术文章"}
    u1.Output()
    // 指针类型调用方法
    u2 := Info{"帽儿山的枪手", "分享技术文章"}
    u3 := &u2
    u3.Output()
}

output

 帽儿山的枪手: 分享技术文章 
帽儿山的枪手: 分享技术文章

anonymous method

If type S contains anonymous field *T , then S and *S method set contains T + *T method.

What this rule says is that when we embed a pointer of a type, methods of the embedded type whose receiver is a value type or pointer type will be promoted and can be called by the value or pointer of the outer type.
 package main

import "fmt"

type S struct {
    T
}

type T struct {
    int
}

func (t T) testT() {
    fmt.Println("如类型 S 包含匿名类型 *T, 则 S 和 *S 方法集包含 T 方法")
}

func (t *T) testP() {
    fmt.Println("如类型 S 包含匿名字段 *T, 则 S 和 *S 方法集合包含 *T 方法")
}

func main() {
    s1 := S{T{1}}
    s2 := &s1
    fmt.Printf("s1 is : %v\n", s1)
    s1.testT()
    s1.testP() // 提升指针类型调用

    fmt.Printf("s2 is : %v\n", s2)
    s2.testT() // 提升值类型调用
    s2.testP()
}

output

 s1 is : {{1}}
如类型 S 包含匿名类型 *T, 则 S 和 *S 方法集包含 T 方法
如类型 S 包含匿名字段 *T, 则 S 和 *S 方法集合包含 *T 方法
s2 is : &{{1}}
如类型 S 包含匿名类型 *T, 则 S 和 *S 方法集包含 T 方法
如类型 S 包含匿名字段 *T, 则 S 和 *S 方法集合包含 *T 方法

expression

According to different callers, methods are divided into two forms

 instance.method(args...) ---> <type>.func(instance, args...)
The former is called method value, and the latter method expression must be passed parameters explicitly.
 package main

import "fmt"

type User struct {
    id   int
    name string
}

func (self *User) Test() {
    fmt.Printf("%p, %v\n", self, self)
}

func main() {
    u := User{1, "帽儿山的枪手"}
    u.Test()

    mValue := u.Test
    mValue() // 隐式传递 receiver

    mExpression := (*User).Test
    mExpression(&u) // 显式传递 receiver
}

output

 0xc00000c018, &{1 帽儿山的枪手}
0xc00000c018, &{1 帽儿山的枪手}
0xc00000c018, &{1 帽儿山的枪手}

In conclusion, the method is a pointer type, and the method value will copy the receiver.

Technical articles are continuously updated, please pay more attention~~

Search WeChat public account【The Gunslinger of Maoer Mountain】, follow me


帽儿山的枪手
71 声望18 粉丝