returns.go
文件信息
- 📄 原文件:
02_returns.go - 🔤 语言:go
完整代码
go
package main
import (
"errors"
"fmt"
"strconv"
)
// ============================================================
// 多返回值与命名返回值
// ============================================================
// Go 函数可以返回多个值,这是 Go 的重要特性
// 最常见的用法是返回 (结果, 错误)
func main02() {
fmt.Println("\n==================== 02_returns ====================")
fmt.Println("=== 多返回值 ===")
// ----------------------------------------------------------
// 基本多返回值
// ----------------------------------------------------------
q, r := divide(17, 5)
fmt.Printf("17 / 5 = %d 余 %d\n", q, r)
// ----------------------------------------------------------
// 忽略部分返回值
// ----------------------------------------------------------
// 使用 _ 忽略不需要的返回值
quotient, _ := divide(20, 3)
fmt.Println("20 / 3 的商:", quotient)
_, remainder := divide(20, 3)
fmt.Println("20 / 3 的余数:", remainder)
// ----------------------------------------------------------
// 返回值 + 错误处理(Go 的惯用模式)
// ----------------------------------------------------------
fmt.Println("\n=== 错误处理模式 ===")
// 【重要】Go 的错误处理惯例:
// - 错误作为最后一个返回值
// - 调用后立即检查错误
// - 错误为 nil 表示成功
result, err := safeDivide(10, 2)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("10 / 2 =", result)
}
result, err = safeDivide(10, 0)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
// 【技巧】if 带初始化的错误检查(更紧凑)
if res, err := safeDivide(100, 4); err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("100 / 4 =", res)
}
// ----------------------------------------------------------
// 命名返回值
// ----------------------------------------------------------
fmt.Println("\n=== 命名返回值 ===")
w, h, area := getRectInfo(5, 3)
fmt.Printf("矩形: 宽=%d, 高=%d, 面积=%d\n", w, h, area)
// 使用裸返回的函数
min, max := getMinMax(3, 7, 1, 9, 4)
fmt.Printf("最小值=%d, 最大值=%d\n", min, max)
// ----------------------------------------------------------
// 命名返回值与 defer
// ----------------------------------------------------------
fmt.Println("\n=== 命名返回值与 defer ===")
fmt.Println("doubleAndLog(5) =", doubleAndLog(5))
// ----------------------------------------------------------
// 实际应用示例
// ----------------------------------------------------------
fmt.Println("\n=== 实际应用示例 ===")
// 解析用户输入
if age, err := parseAge("25"); err != nil {
fmt.Println("解析失败:", err)
} else {
fmt.Println("年龄:", age)
}
if age, err := parseAge("abc"); err != nil {
fmt.Println("解析失败:", err)
} else {
fmt.Println("年龄:", age)
}
if age, err := parseAge("-5"); err != nil {
fmt.Println("解析失败:", err)
} else {
fmt.Println("年龄:", age)
}
// ----------------------------------------------------------
// 多返回值解构
// ----------------------------------------------------------
fmt.Println("\n=== 多返回值解构 ===")
// 可以在一行中处理多个返回值
s1, s2 := swap(1, 2)
fmt.Printf("swap(1, 2) = %d, %d\n", s1, s2)
// 链式赋值
a, b := 1, 2
a, b = swap(a, b)
fmt.Printf("交换后: a=%d, b=%d\n", a, b)
}
// ============================================================
// 多返回值函数定义
// ============================================================
// ----------------------------------------------------------
// 基本多返回值
// ----------------------------------------------------------
// 语法: func 函数名(参数) (类型1, 类型2, ...) { return 值1, 值2, ... }
// 【注意】多返回值的类型要用括号包裹
func divide(dividend, divisor int) (int, int) {
quotient := dividend / divisor
remainder := dividend % divisor
return quotient, remainder
}
// ----------------------------------------------------------
// 带错误返回
// ----------------------------------------------------------
// 【惯例】错误作为最后一个返回值
// 【惯例】成功时 error 为 nil,失败时返回错误信息
func safeDivide(a, b int) (int, error) {
if b == 0 {
// 返回零值和错误
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// ----------------------------------------------------------
// 命名返回值
// ----------------------------------------------------------
// 语法: func 函数名(参数) (名1 类型1, 名2 类型2) { ... }
//
// 【特点】
// 1. 命名返回值会被初始化为零值
// 2. 可以像普通变量一样使用
// 3. 可以使用"裸返回"(naked return)
//
// 【优点】
// - 自动初始化,减少变量声明
// - 提供返回值的文档说明
// - 某些情况下代码更简洁
//
// 【缺点】
// - 裸返回可能降低代码可读性
// - 容易引起变量遮蔽问题
func getRectInfo(width, height int) (w int, h int, area int) {
// 命名返回值已自动初始化为 0
w = width
h = height
area = width * height
return // 裸返回,返回 w, h, area 的当前值
}
// ----------------------------------------------------------
// 命名返回值 + 裸返回
// ----------------------------------------------------------
// 【警告】裸返回在长函数中可能降低可读性
// 【建议】短函数可用裸返回,长函数建议显式返回
func getMinMax(nums ...int) (min, max int) {
if len(nums) == 0 {
return // 返回零值 0, 0
}
min = nums[0]
max = nums[0]
for _, n := range nums[1:] {
if n < min {
min = n
}
if n > max {
max = n
}
}
return // 裸返回
}
// ----------------------------------------------------------
// 命名返回值 + defer
// ----------------------------------------------------------
// 【重要】defer 可以修改命名返回值!
// 这是因为 defer 在 return 语句之后执行,但在函数返回之前
func doubleAndLog(n int) (result int) {
defer func() {
fmt.Printf(" [defer] 原值=%d, 返回值=%d\n", n, result)
// 可以在这里修改 result
// result = result + 1 // 这会改变最终返回值
}()
result = n * 2
return // 返回 result 的值
}
// ----------------------------------------------------------
// 实际应用:解析并验证
// ----------------------------------------------------------
// 【模式】解析 + 验证 + 错误返回
func parseAge(s string) (int, error) {
// 1. 解析
age, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("无法解析 '%s' 为数字: %w", s, err)
}
// 2. 验证
if age < 0 {
return 0, errors.New("年龄不能为负数")
}
if age > 150 {
return 0, errors.New("年龄不能超过150")
}
// 3. 返回结果
return age, nil
}
// ----------------------------------------------------------
// 交换值
// ----------------------------------------------------------
// 【技巧】Go 的多返回值让交换变得简单
func swap(a, b int) (int, int) {
return b, a
}
// ============================================================
// 重要注意事项
// ============================================================
//
// 1. 【错误处理惯例】
// result, err := someFunc()
// if err != nil {
// // 处理错误
// return err // 或其他错误处理
// }
// // 使用 result
//
// 2. 【不要忽略错误】
// result, _ := someFunc() // 危险!除非你确定不会出错
//
// 3. 【命名返回值陷阱】
// func foo() (result int) {
// result := 10 // 错误!这创建了新的局部变量,遮蔽了命名返回值
// result = 10 // 正确!这赋值给命名返回值
// return
// }
//
// 4. 【何时使用命名返回值】
// - 函数有多个返回值,需要文档说明
// - 需要在 defer 中修改返回值
// - 短函数,裸返回不影响可读性
//
// 5. 【何时避免裸返回】
// - 长函数(超过几行)
// - 有多个 return 点
// - 返回值逻辑复杂
💬 讨论
使用 GitHub 账号登录后即可参与讨论