NOTE
1.5 function
What functions are in Go, variadic parameters, multiple return values, named returns, closures, init functions, methods, stacks, and common functions.
This is a historical learning note and may contain outdated or incomplete understanding.
1. What It Is
- Functions are first-class citizens
- There are named functions and anonymous functions
- Binding a function to a struct turns it into a method
2. Usage
2.1. Variadic Parameters
func Print(a ...interface{}) {
fmt.Println(a...)
}
func TestFunction1(t *testing.T) {
var a = []interface{}{123, "abc"}
Print(a...) // 123 abc
Print(a) // [123 abc]
}
2.2. Multiple Return Values
Go functions can return multiple values.
func TestFunction2(t *testing.T) {
fmt.Println(multiRes(0))
fmt.Println(multiRes(1))
}
func multiRes(val int) (res int, err error) {
if val == 0 {
return val * 2, nil
}
// If the return parameters are named, they can be returned directly
return
}
// output
0 <nil>
2 <nil>
2.2.1. Implementation
- C implementation Allocate space for return values in the calling function, then pass pointers to the return values to the called function.
// calling function
int ret1, ret2;
f(a, b, &ret1, &ret2)
// called function
void f(int arg1, int arg2, int *ret1, int *ret2
- Go implementation Reserve space for the return values above the input parameters.
func f(arg1, arg2 int) (ret1, ret2 int)
Assembly layout:
space reserved for ret2
space reserved for ret1
parameter 2
parameter 1 <-SP
2.3. Return Values Can Be Named
func TestFunction3(t *testing.T) {
fmt.Println(namedRes(1))
}
func namedRes(i int) (res int, err error) {
res = i * 2
err = fmt.Errorf("error...")
return
}
// output
2 error...
2.4. Closures
2.4.1. What They Are
- An inner function references a variable from an outer function, and after the outer function returns, the inner function can still use that variable. This is called a closure
- Anonymous function + references a variable in an outer scope = closure function
2.4.2. Usage
2.4.2.1. A closure accesses outer variables by reference
func TestFunction5(t *testing.T) {
var i int = 10
func() {
i++
}()
fmt.Println(i)
}
// output
11
2.4.2.2. A closure is evaluated later
func TestFunction6(t *testing.T) {
for i := 0; i < 3; i++ {
defer func(){ println(i) } ()
}
}
// Output:
// 3
// 3
// 3
- Modification
func TestFunction6(t *testing.T) {
for i := 0; i < 3; i++ {
i := i // Define a local variable i inside the loop body
defer func(){ println(i) } ()
}
}
// Or
func TestFunction6(t *testing.T) {
for i := 0; i < 3; i++ {
// Pass i through the function argument
// The defer statement evaluates call arguments immediately
defer func(i int){ println(i) } (i)
}
}
2.5. init Function
- init1.go
// Executed before init
var aa string = getAa()
func getAa() string {
fmt.Println("aaaaaaaaaa")
return "eeeeee"
}
func init() {
fmt.Println("init")
}
- function1_test.go
func TestFunction4(t *testing.T) {
fmt.Println(aa)
}
// output
aaaaaaaaaa
init
eeeeee
2.6. Methods
type Student struct {
Name string
}
func (s Student) learn() {
fmt.Println(s.Name + " is learning")
}
func TestMethod(t *testing.T) {
s := &Student{
Name: "user",
}
s.learn()
}
// output
user is learning
3. Function Stack
- Go’s stack changes dynamically. Initially it allocates only 4 or 8 KB, and it can grow to the GB level.
- Before Go 1.4, a linked list was used to implement dynamic stacks; afterward, arrays were used.
4. Common Functions
4.1. print
func testPrint() {
fmt.Printf("Hello %d\n", 23) // Used for formatted output
fmt.Fprint(os.Stdout, "Hello ", 23, "\n") // Used when changing the file descriptor
fmt.Println("Hello", 23) // Used for concatenated output
s := fmt.Sprint("Hello ", 23) // Used by String()
fmt.Println(s)
}
4.2. len vs cap
func Test1(t *testing.T) {
strings := make([]string, 0, 10)
fmt.Println(len(strings), cap(strings))
strings = append(strings, "a")
fmt.Println(len(strings), cap(strings))
}
// output
0 10
1 10
Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub