文章目录
- 一、函数的基本定义
- 
- [1.1 多返回值](#1.1 多返回值)
- [1.2 命名返回值](#1.2 命名返回值)
 
- 二、函数的参数
- 
- [2.1 值传递与 指针传递](#2.1 值传递与 指针传递)
- [2.2 可变参数](#2.2 可变参数)
 
- 三、函数类型与高阶函数
- 
- [3.1 函数类型](#3.1 函数类型)
- [3.2 匿名函数lambda 与 闭包](#3.2 匿名函数lambda 与 闭包)
 
- 四、方法(成员函数)
- 
- [4.1 方法的定义](#4.1 方法的定义)
- [4.2 指针接收者](#4.2 指针接收者)
 
- 五、错误处理
- 
- [5.1 错误类型](#5.1 错误类型)
- [5.2 错误返回值](#5.2 错误返回值)
- [5.3 `if` 语句检查错误](#5.3 if语句检查错误)
- [5.4 自定义错误类型](#5.4 自定义错误类型)
- [5.5 `fmt.Errorf` 创建错误](#5.5 fmt.Errorf创建错误)
- [5.6 多重错误检查](#5.6 多重错误检查)
- [5.7 `panic` 和 `recover` 机制](#5.7 panic和recover机制)
- [5.8 `defer` 用于资源清理](#5.8 defer用于资源清理)
 
一、函数的基本定义
在Go语言中,通过func定义函数,基本的函数定义如下:
            
            
              go
              
              
            
          
          func add(a int, b int) int {
	return a + b
}- func:关键字,用于定义函数
- add:函数名
- (a int, b int):参数列表,参数类型在参数名之后
- int:返回值类型。
- return a + b:函数体,返回两个整数的和。
1.1 多返回值
Go语言函数支持多返回值,一般在处理错误,或者返回多个结果时使用:
            
            
              go
              
              
            
          
          func swap(x, y string) (string, string) {
    return y, x
}1.2 命名返回值
Go语言允许为返回值命名,命名后函数体中可以直接使用这些变量,最后直接return即可
            
            
              go
              
              
            
          
          func divide(a, b float64) (result float64, err error) {
	if b == 0 {
		err = errors.New("division by zero")
		return
	}
}二、函数的参数
2.1 值传递与 指针传递
Go中参数传递默认是值传递,即函数内部对变量的修改不会影响外部变量,如果想使函数内部的修改同时影响外部变量,可以传指针(Go没有类似C++那样直接传引用的方法)
            
            
              go
              
              
            
          
          func modifyValue(x int) {
	x = 100;
}
func modifyPointer(x *int) {
	*x = 100
}2.2 可变参数
Go中,通过 ... 表示变长参数(可变参数),允许在函数中传递一个不确定数量的参数。
            
            
              go
              
              
            
          
          func sum(nums ...int) int {
	total := 0
	for _, num := range nums {
		total += num
	}
	return total
}- nums ...int表示- sum函数可以接受 任意数量 的 int 类型参数。可以传递零个、一个或多个 int 参数。
- 在函数体内,nums 是一个 切片 ([]int),它包含了传递给 sum 函数的所有参数。
- ... 后面跟的是参数类型,表示这个参数的类型是一个切片类型,可以接收任意数量的元素。
三、函数类型与高阶函数
Go语言中,函数本质也是一种类型,可以同其他类型一样被传递与使用。
3.1 函数类型
我们可以通过type关键字将一个函数定义为一种类型,从而被传递/使用。
            
            
              go
              
              
            
          
          // 定于函数类型
type operation func(int, int) int
func apply(op operation, a, b int) int {
	return op(a, b)
}对于上面的代码,operation 是一个 类型别名,代表的是一个函数类型,函数 apply 接受一个 operation 类型的 op 对象,可以直接在函数体内调用op。
3.2 匿名函数lambda 与 闭包
Go中也有lambda函数(匿名函数),如下:
            
            
              go
              
              
            
          
          add := func(a, b int) int {
	return a + b
}- 这行代码定义了一个 匿名函数,即没有函数名的函数。
- func(a, b int) int表示该函数接收 两个- int类型的参数 ,并返回一个- int类型的结果。
- 函数体内部的 return a + b表示返回a和b的和。
- add :=将这个匿名函数赋值给了变量- add,因此- add现在是一个函数类型的变量,且类型为- func(int, int) int。
匿名函数的作用
- 匿名函数是一种没有函数名的函数,通常用于临时的、一次性的操作,或者在需要将函数作为参数传递时使用。
- 匿名函数通常用于实现闭包
闭包
闭包是指一个函数捕获了其外部作用域的变量。
            
            
              go
              
              
            
          
          func adder() func(a, b int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}	
}
func main() {
	a := adder()
	fmt.Println(a(1)) // 输出 1
    fmt.Println(a(2)) // 输出 3
}- sum变量是 闭包 中的一个状态,匿名函数每次调用时都能"记住"之前的- sum值。
- 即使 adder函数已经返回,匿名函数仍然可以访问并修改sum,因为它持有对外部sum变量的引用
四、方法(成员函数)
Go语言中没有类的概念,但可以为结构体定义方法。
4.1 方法的定义
            
            
              go
              
              
            
          
          type Rectangle struct {
	width, height float64
}
func (r Rectangle) area() float64 {
	return r.width * r.height
}对于上面的代码:
- func (r Rectangle) area() float64这行代码定义了一个 方法 ,该方法与- Rectangle类型关联。- (r Rectangle)是方法的 接收者 ,表示- area方法是为- Rectangle类型定义的。这里的- r是一个- Rectangle类型的值,它代表一个矩形对象。
- area()是方法的名称,表示该方法会计算矩形的面积。
- float64是方法的返回值类型,表示计算出的面积是一个- float64类型的值。
 
4.2 指针接收者
同理我们可以将接收者改为指针方式:
            
            
              go
              
              
            
          
          func (r* Rectangle) scale(factor float64) {
	r.width *= factor
	r.height *= factor
}- func (r *Rectangle) scale(factor float64):这是一个方法定义。- (r *Rectangle)是 接收者 (receiver),意味着- scale是为- Rectangle类型的指针(- *Rectangle)定义的方法。使用指针接收者而不是值接收者,可以让该方法直接修改原始对象(结构体)本身的字段。
- factor float64是- scale方法的参数,表示缩放因子,用于调整矩形的大小。
 
五、错误处理
Go 语言的错误处理与其他语言有些不同,Go 提倡显式的错误处理,避免隐式的异常机制。Go 没有传统的 try-catch 语句,而是通过函数返回值来传递错误,程序员需要手动检查和处理这些错误。
5.1 错误类型
在 Go 中,错误通常是 error 类型的一个值。error 是一个内建的接口类型,定义如下:
            
            
              go
              
              
            
          
          type error interface {
    Error() string
}error 接口只有一个方法 Error() string,它返回一个描述错误的字符串。许多标准库和自定义函数都会返回这个类型的值,以指示操作是否成功。
5.2 错误返回值
在 Go 中,函数通常会返回两个值:一个是主要结果值,另一个是错误值。一般要求我们显式地检查错误值。比如:
            
            
              go
              
              
            
          
          func someFunction() (int, error) {
    return 0, fmt.Errorf("something went wrong")
}在调用时,返回的错误必须检查:
            
            
              go
              
              
            
          
          result, err := someFunction()
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Println("Result:", result)
}5.3 if 语句检查错误
根据 Go 的错误处理风格,一般我们显式地检查每一个函数调用的返回值中的 error:
            
            
              go
              
              
            
          
          func example() {
    file, err := os.Open("nonexistent_file.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return // 处理错误后,可能会退出函数或做其他错误处理
    }
    defer file.Close()
    // 进一步的操作
}在这里,如果 os.Open 返回一个错误(例如文件不存在),我们就会打印错误并退出函数。if err != nil 是 Go 错误处理的常见模式。
5.4 自定义错误类型
Go 支持自定义错误类型,通过实现 Error() 方法,可以创建自己的错误类型。这样可以携带更多的错误信息,比如错误代码、详细描述等。
            
            
              go
              
              
            
          
          type MyError struct {
    Code    int
    Message string
}
func (e *MyError) Error() string {
    return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
func doSomething() error {
    return &MyError{Code: 404, Message: "Not Found"}
}
func main() {
    err := doSomething()
    if err != nil {
        fmt.Println("Error:", err)
    }
}对于上面的代码,MyError 结构体包含了一个错误码和错误消息,我们通过 Error() 方法实现了 error 接口。
5.5 fmt.Errorf 创建错误
Go 提供了 fmt.Errorf 函数来创建错误并格式化错误信息:
            
            
              go
              
              
            
          
          err := fmt.Errorf("something went wrong: %v", someVar)可以用于动态生成带有变量信息的错误消息。
5.6 多重错误检查
有时,我们需要同时检查多个函数调用的错误:
            
            
              go
              
              
            
          
          file, err := os.Open("file.txt")
if err != nil {
    fmt.Println("Error opening file:", err)
    return
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil {
    fmt.Println("Error reading file:", err)
    return
}每个函数调用后都要检查错误,并根据需要处理它。
5.7 panic 和 recover 机制
虽然 Go 没有 try-catch 语法,但 Go 提供了 panic 和 recover 来实现类似异常处理的机制。panic 用于在程序出现无法恢复的错误时终止执行,而 recover 则用于捕获和处理 panic。
            
            
              go
              
              
            
          
          func riskyFunction() {
    panic("Something went wrong!")
}
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    riskyFunction() // 此调用将导致 panic 被 recover 捕获
}5.8 defer 用于资源清理
在 Go 中,defer 语句用于在函数退出时执行某些操作,通常用于资源清理,如关闭文件、解锁资源等。即使函数中发生了错误,defer 语句仍会被执行。
            
            
              go
              
              
            
          
          func readFile() (string, error) {
    file, err := os.Open("file.txt")
    if err != nil {
        return "", err
    }
    defer file.Close() // 确保文件在函数退出时关闭
    content, err := ioutil.ReadAll(file)
    if err != nil {
        return "", err
    }
    return string(content), nil
}在这个例子中,即使读取文件时出现错误,defer file.Close() 仍会确保文件被正确关闭。