Files
golang/golang-learning/03-functions/02-multiple-returns.go
2025-08-24 01:01:26 +08:00

774 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
02-multiple-returns.go - Go 语言多返回值详解
学习目标:
1. 掌握多返回值的语法和用法
2. 理解命名返回值的优势
3. 学会错误处理的惯用模式
4. 了解多返回值的实际应用场景
5. 掌握返回值的最佳实践
知识点:
- 多返回值语法
- 命名返回值
- 错误处理模式
- 返回值解构
- 忽略返回值
- 多返回值的性能考虑
- 实际应用场景
*/
package main
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"time"
)
func main() {
fmt.Println("=== Go 语言多返回值详解 ===\n")
// 演示基本多返回值
demonstrateBasicMultipleReturns()
// 演示命名返回值
demonstrateNamedReturns()
// 演示错误处理模式
demonstrateErrorHandling()
// 演示返回值解构和忽略
demonstrateReturnValueHandling()
// 演示多返回值的实际应用
demonstratePracticalApplications()
// 演示多返回值的高级用法
demonstrateAdvancedUsage()
// 演示性能考虑
demonstratePerformanceConsiderations()
}
// demonstrateBasicMultipleReturns 演示基本多返回值
func demonstrateBasicMultipleReturns() {
fmt.Println("1. 基本多返回值:")
// 两个返回值
fmt.Printf(" 两个返回值:\n")
quotient, remainder := divmod(17, 5)
fmt.Printf(" 17 ÷ 5 = %d 余 %d\n", quotient, remainder)
// 三个返回值
fmt.Printf(" 三个返回值:\n")
min, max, avg := analyzeNumbers([]int{3, 7, 1, 9, 4, 6})
fmt.Printf(" 数组 [3, 7, 1, 9, 4, 6] - 最小值: %d, 最大值: %d, 平均值: %.2f\n", min, max, avg)
// 不同类型的返回值
fmt.Printf(" 不同类型的返回值:\n")
name, age, height, isStudent := getPersonInfo()
fmt.Printf(" 个人信息 - 姓名: %s, 年龄: %d, 身高: %.1fcm, 学生: %t\n",
name, age, height, isStudent)
// 返回值和错误
fmt.Printf(" 返回值和错误:\n")
result, err := safeDivide(10, 2)
if err != nil {
fmt.Printf(" 错误: %v\n", err)
} else {
fmt.Printf(" 10 ÷ 2 = %.2f\n", result)
}
result, err = safeDivide(10, 0)
if err != nil {
fmt.Printf(" 错误: %v\n", err)
} else {
fmt.Printf(" 10 ÷ 0 = %.2f\n", result)
}
fmt.Println()
}
// demonstrateNamedReturns 演示命名返回值
func demonstrateNamedReturns() {
fmt.Println("2. 命名返回值:")
// 基本命名返回值
fmt.Printf(" 基本命名返回值:\n")
area, perimeter := calculateRectangle(5, 3)
fmt.Printf(" 矩形 5×3 - 面积: %d, 周长: %d\n", area, perimeter)
// 命名返回值的提前返回
fmt.Printf(" 命名返回值的提前返回:\n")
score, grade, pass := evaluateScore(85)
fmt.Printf(" 分数 85 - 等级: %s, 是否及格: %t\n", grade, pass)
score, grade, pass = evaluateScore(45)
fmt.Printf(" 分数 45 - 等级: %s, 是否及格: %t\n", grade, pass)
// 复杂的命名返回值
fmt.Printf(" 复杂的命名返回值:\n")
valid, reason, suggestions := validatePassword("123")
fmt.Printf(" 密码 \"123\" - 有效: %t, 原因: %s\n", valid, reason)
if len(suggestions) > 0 {
fmt.Printf(" 建议: %v\n", suggestions)
}
valid, reason, suggestions = validatePassword("SecurePass123!")
fmt.Printf(" 密码 \"SecurePass123!\" - 有效: %t, 原因: %s\n", valid, reason)
fmt.Println()
}
// demonstrateErrorHandling 演示错误处理模式
func demonstrateErrorHandling() {
fmt.Println("3. 错误处理模式:")
// 标准错误处理模式
fmt.Printf(" 标准错误处理模式:\n")
// 字符串转整数
value, err := parseInteger("123")
if err != nil {
fmt.Printf(" 解析错误: %v\n", err)
} else {
fmt.Printf(" 解析成功: %d\n", value)
}
value, err = parseInteger("abc")
if err != nil {
fmt.Printf(" 解析错误: %v\n", err)
} else {
fmt.Printf(" 解析成功: %d\n", value)
}
// 文件操作模拟
fmt.Printf(" 文件操作模拟:\n")
content, err := readFile("config.txt")
if err != nil {
fmt.Printf(" 读取文件错误: %v\n", err)
} else {
fmt.Printf(" 文件内容: %s\n", content)
}
// 网络请求模拟
fmt.Printf(" 网络请求模拟:\n")
data, statusCode, err := httpGet("https://api.example.com/users")
if err != nil {
fmt.Printf(" 请求错误: %v\n", err)
} else {
fmt.Printf(" 状态码: %d, 数据长度: %d\n", statusCode, len(data))
}
// 多层错误处理
fmt.Printf(" 多层错误处理:\n")
result, err := processUserData("user123")
if err != nil {
fmt.Printf(" 处理用户数据错误: %v\n", err)
} else {
fmt.Printf(" 处理结果: %s\n", result)
}
fmt.Println()
}
// demonstrateReturnValueHandling 演示返回值解构和忽略
func demonstrateReturnValueHandling() {
fmt.Println("4. 返回值解构和忽略:")
// 接收所有返回值
fmt.Printf(" 接收所有返回值:\n")
x, y, z := getCoordinates()
fmt.Printf(" 坐标: (%d, %d, %d)\n", x, y, z)
// 忽略某些返回值
fmt.Printf(" 忽略某些返回值:\n")
_, _, avgZ := getCoordinates()
fmt.Printf(" 只关心 Z 坐标: %d\n", avgZ)
// 只检查错误
fmt.Printf(" 只检查错误:\n")
_, err := safeDivide(10, 2)
if err != nil {
fmt.Printf(" 操作失败: %v\n", err)
} else {
fmt.Printf(" 操作成功\n")
}
// 链式调用
fmt.Printf(" 链式调用:\n")
result, err := processChain("input")
if err != nil {
fmt.Printf(" 链式处理错误: %v\n", err)
} else {
fmt.Printf(" 链式处理结果: %s\n", result)
}
// 多重赋值
fmt.Printf(" 多重赋值:\n")
a, b := 10, 20
fmt.Printf(" 交换前: a=%d, b=%d\n", a, b)
a, b = swap(a, b)
fmt.Printf(" 交换后: a=%d, b=%d\n", a, b)
fmt.Println()
}
// demonstratePracticalApplications 演示多返回值的实际应用
func demonstratePracticalApplications() {
fmt.Println("5. 多返回值的实际应用:")
// 应用1: 数据库查询模拟
fmt.Printf(" 应用1 - 数据库查询模拟:\n")
user, found, err := findUserByID(123)
if err != nil {
fmt.Printf(" 查询错误: %v\n", err)
} else if !found {
fmt.Printf(" 用户不存在\n")
} else {
fmt.Printf(" 找到用户: %+v\n", user)
}
// 应用2: 缓存操作
fmt.Printf(" 应用2 - 缓存操作:\n")
value, hit, err := getFromCache("user:123")
if err != nil {
fmt.Printf(" 缓存错误: %v\n", err)
} else if hit {
fmt.Printf(" 缓存命中: %s\n", value)
} else {
fmt.Printf(" 缓存未命中\n")
}
// 应用3: 配置解析
fmt.Printf(" 应用3 - 配置解析:\n")
config, warnings, err := parseConfig("app.conf")
if err != nil {
fmt.Printf(" 配置解析错误: %v\n", err)
} else {
fmt.Printf(" 配置加载成功: %+v\n", config)
if len(warnings) > 0 {
fmt.Printf(" 警告: %v\n", warnings)
}
}
// 应用4: 数据验证
fmt.Printf(" 应用4 - 数据验证:\n")
userData := map[string]string{
"name": "Alice",
"email": "alice@example.com",
"age": "25",
}
valid, errors := validateUserData(userData)
fmt.Printf(" 用户数据验证 - 有效: %t\n", valid)
if len(errors) > 0 {
fmt.Printf(" 验证错误: %v\n", errors)
}
// 应用5: 统计分析
fmt.Printf(" 应用5 - 统计分析:\n")
data := []float64{1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0}
mean, median, stddev := calculateStatistics(data)
fmt.Printf(" 数据: %v\n", data)
fmt.Printf(" 统计结果 - 平均值: %.2f, 中位数: %.2f, 标准差: %.2f\n",
mean, median, stddev)
fmt.Println()
}
// demonstrateAdvancedUsage 演示多返回值的高级用法
func demonstrateAdvancedUsage() {
fmt.Println("6. 多返回值的高级用法:")
// 返回函数
fmt.Printf(" 返回函数:\n")
add, multiply := getMathOperations()
fmt.Printf(" add(5, 3) = %d\n", add(5, 3))
fmt.Printf(" multiply(5, 3) = %d\n", multiply(5, 3))
// 返回接口
fmt.Printf(" 返回接口:\n")
reader, writer := getIOOperations()
data := []byte("Hello, World!")
n, err := writer.Write(data)
if err != nil {
fmt.Printf(" 写入错误: %v\n", err)
} else {
fmt.Printf(" 写入 %d 字节\n", n)
}
buffer := make([]byte, 13)
n, err = reader.Read(buffer)
if err != nil {
fmt.Printf(" 读取错误: %v\n", err)
} else {
fmt.Printf(" 读取 %d 字节: %s\n", n, string(buffer[:n]))
}
// 返回通道
fmt.Printf(" 返回通道:\n")
input, output := createPipeline()
// 发送数据
go func() {
for i := 1; i <= 3; i++ {
input <- i
}
close(input)
}()
// 接收处理后的数据
for result := range output {
fmt.Printf(" 处理结果: %d\n", result)
}
fmt.Println()
}
// demonstratePerformanceConsiderations 演示性能考虑
func demonstratePerformanceConsiderations() {
fmt.Println("7. 性能考虑:")
// 返回值的内存分配
fmt.Printf(" 返回值的内存分配:\n")
// 返回值拷贝 vs 返回指针
largeData := make([]int, 1000)
for i := range largeData {
largeData[i] = i
}
// 返回拷贝(可能影响性能)
copied, size := copyLargeData(largeData)
fmt.Printf(" 返回拷贝 - 大小: %d, 第一个元素: %d\n", size, copied[0])
// 返回指针(更高效)
ptr, size := referenceLargeData(largeData)
fmt.Printf(" 返回指针 - 大小: %d, 第一个元素: %d\n", size, (*ptr)[0])
// 多返回值 vs 结构体
fmt.Printf(" 多返回值 vs 结构体:\n")
// 使用多返回值
name, age, email := getUserInfo1()
fmt.Printf(" 多返回值: %s, %d, %s\n", name, age, email)
// 使用结构体
user := getUserInfo2()
fmt.Printf(" 结构体: %s, %d, %s\n", user.Name, user.Age, user.Email)
// 错误处理的性能
fmt.Printf(" 错误处理的性能:\n")
// 正常情况
start := time.Now()
for i := 0; i < 1000; i++ {
_, err := fastOperation(i)
if err != nil {
// 处理错误
}
}
duration := time.Since(start)
fmt.Printf(" 1000次正常操作耗时: %v\n", duration)
fmt.Println()
}
// ========== 函数定义 ==========
// 基本多返回值函数
func divmod(a, b int) (int, int) {
return a / b, a % b
}
func analyzeNumbers(numbers []int) (int, int, float64) {
if len(numbers) == 0 {
return 0, 0, 0
}
min, max := numbers[0], numbers[0]
sum := 0
for _, num := range numbers {
if num < min {
min = num
}
if num > max {
max = num
}
sum += num
}
avg := float64(sum) / float64(len(numbers))
return min, max, avg
}
func getPersonInfo() (string, int, float64, bool) {
return "Alice", 25, 165.5, true
}
func safeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 命名返回值函数
func calculateRectangle(width, height int) (area, perimeter int) {
area = width * height
perimeter = 2 * (width + height)
return // 自动返回命名的返回值
}
func evaluateScore(score int) (originalScore int, grade string, pass bool) {
originalScore = score
if score < 0 || score > 100 {
grade = "无效"
return // 提前返回
}
switch {
case score >= 90:
grade = "A"
case score >= 80:
grade = "B"
case score >= 70:
grade = "C"
case score >= 60:
grade = "D"
default:
grade = "F"
}
pass = score >= 60
return
}
func validatePassword(password string) (valid bool, reason string, suggestions []string) {
if len(password) < 8 {
reason = "密码长度不足8位"
suggestions = append(suggestions, "增加密码长度")
return
}
hasUpper := strings.ContainsAny(password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
hasLower := strings.ContainsAny(password, "abcdefghijklmnopqrstuvwxyz")
hasDigit := strings.ContainsAny(password, "0123456789")
hasSpecial := strings.ContainsAny(password, "!@#$%^&*")
if !hasUpper {
suggestions = append(suggestions, "添加大写字母")
}
if !hasLower {
suggestions = append(suggestions, "添加小写字母")
}
if !hasDigit {
suggestions = append(suggestions, "添加数字")
}
if !hasSpecial {
suggestions = append(suggestions, "添加特殊字符")
}
if hasUpper && hasLower && hasDigit && hasSpecial {
valid = true
reason = "密码强度良好"
} else {
reason = "密码强度不足"
}
return
}
// 错误处理函数
func parseInteger(s string) (int, error) {
value, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("无法解析整数 '%s': %w", s, err)
}
return value, nil
}
func readFile(filename string) (string, error) {
// 模拟文件读取
if filename == "config.txt" {
return "server_port=8080\ndebug=true", nil
}
return "", fmt.Errorf("文件 '%s' 不存在", filename)
}
func httpGet(url string) ([]byte, int, error) {
// 模拟HTTP请求
if strings.Contains(url, "example.com") {
return []byte(`{"users": [{"id": 1, "name": "Alice"}]}`), 200, nil
}
return nil, 0, errors.New("网络连接失败")
}
func processUserData(userID string) (string, error) {
// 模拟多层处理
if userID == "" {
return "", errors.New("用户ID不能为空")
}
// 模拟数据库查询
if userID == "user123" {
return "用户数据处理完成", nil
}
return "", fmt.Errorf("用户 '%s' 不存在", userID)
}
// 返回值处理函数
func getCoordinates() (int, int, int) {
return 10, 20, 30
}
func processChain(input string) (string, error) {
if input == "" {
return "", errors.New("输入不能为空")
}
// 模拟处理链
step1 := strings.ToUpper(input)
step2 := step1 + "_PROCESSED"
return step2, nil
}
func swap(a, b int) (int, int) {
return b, a
}
// 实际应用函数
type User struct {
ID int
Name string
Email string
}
func findUserByID(id int) (User, bool, error) {
// 模拟数据库查询
if id <= 0 {
return User{}, false, errors.New("无效的用户ID")
}
if id == 123 {
return User{ID: 123, Name: "Alice", Email: "alice@example.com"}, true, nil
}
return User{}, false, nil
}
func getFromCache(key string) (string, bool, error) {
// 模拟缓存操作
cache := map[string]string{
"user:123": "Alice",
"user:456": "Bob",
}
if value, exists := cache[key]; exists {
return value, true, nil
}
return "", false, nil
}
type Config struct {
Port int
Debug bool
Timeout int
}
func parseConfig(filename string) (Config, []string, error) {
// 模拟配置解析
config := Config{Port: 8080, Debug: true, Timeout: 30}
warnings := []string{"使用默认超时值"}
if filename == "app.conf" {
return config, warnings, nil
}
return Config{}, nil, fmt.Errorf("配置文件 '%s' 不存在", filename)
}
func validateUserData(data map[string]string) (bool, []string) {
var errors []string
if name := data["name"]; name == "" {
errors = append(errors, "姓名不能为空")
}
if email := data["email"]; !strings.Contains(email, "@") {
errors = append(errors, "邮箱格式无效")
}
if ageStr := data["age"]; ageStr != "" {
if age, err := strconv.Atoi(ageStr); err != nil || age < 0 || age > 150 {
errors = append(errors, "年龄无效")
}
}
return len(errors) == 0, errors
}
func calculateStatistics(data []float64) (mean, median, stddev float64) {
if len(data) == 0 {
return 0, 0, 0
}
// 计算平均值
sum := 0.0
for _, v := range data {
sum += v
}
mean = sum / float64(len(data))
// 计算中位数(简化版)
median = data[len(data)/2]
// 计算标准差
variance := 0.0
for _, v := range data {
variance += (v - mean) * (v - mean)
}
stddev = math.Sqrt(variance / float64(len(data)))
return
}
// 高级用法函数
func getMathOperations() (func(int, int) int, func(int, int) int) {
add := func(a, b int) int { return a + b }
multiply := func(a, b int) int { return a * b }
return add, multiply
}
type MockReader struct {
data []byte
pos int
}
func (r *MockReader) Read(p []byte) (n int, error) {
if r.pos >= len(r.data) {
return 0, errors.New("EOF")
}
n = copy(p, r.data[r.pos:])
r.pos += n
return n, nil
}
type MockWriter struct {
data []byte
}
func (w *MockWriter) Write(p []byte) (n int, error) {
w.data = append(w.data, p...)
return len(p), nil
}
func getIOOperations() (*MockReader, *MockWriter) {
reader := &MockReader{data: []byte("Hello, World!")}
writer := &MockWriter{}
return reader, writer
}
func createPipeline() (chan<- int, <-chan int) {
input := make(chan int)
output := make(chan int)
go func() {
defer close(output)
for value := range input {
output <- value * 2 // 处理数据
}
}()
return input, output
}
// 性能考虑函数
func copyLargeData(data []int) ([]int, int) {
copied := make([]int, len(data))
copy(copied, data)
return copied, len(copied)
}
func referenceLargeData(data []int) (*[]int, int) {
return &data, len(data)
}
func getUserInfo1() (string, int, string) {
return "Alice", 25, "alice@example.com"
}
type UserInfo struct {
Name string
Age int
Email string
}
func getUserInfo2() UserInfo {
return UserInfo{Name: "Alice", Age: 25, Email: "alice@example.com"}
}
func fastOperation(n int) (int, error) {
if n < 0 {
return 0, errors.New("负数")
}
return n * 2, nil
}
/*
运行这个程序:
go run 02-multiple-returns.go
学习要点:
1. Go 支持多返回值,这是其独特且强大的特性
2. 多返回值常用于返回结果和错误信息
3. 命名返回值可以提高代码可读性,支持提前返回
4. 可以使用 _ 忽略不需要的返回值
5. 多返回值是 Go 错误处理的基础
多返回值的优势:
1. 避免使用指针参数传递多个结果
2. 使错误处理更加明确和一致
3. 提高代码的可读性和维护性
4. 支持函数式编程风格
常见模式:
1. (result, error) - 标准错误处理模式
2. (value, ok) - 检查操作是否成功
3. (value, found, error) - 查询操作模式
4. 命名返回值 - 提高可读性
最佳实践:
1. 错误通常作为最后一个返回值
2. 使用命名返回值提高复杂函数的可读性
3. 适当使用 _ 忽略不需要的返回值
4. 保持返回值数量合理通常不超过3-4个
5. 考虑使用结构体替代过多的返回值
性能考虑:
1. 多返回值通常比传递指针参数更高效
2. 大对象考虑返回指针而不是值拷贝
3. 命名返回值可能有轻微的性能开销
4. 错误处理的性能影响通常可以忽略
应用场景:
1. 错误处理
2. 数据库查询
3. 网络操作
4. 文件操作
5. 数据验证
6. 统计计算
7. 缓存操作
*/