引言

函数是程序的基本构建块,它将代码组织成可重用的逻辑单元。在Rust中,函数不仅是代码组织的方式,还体现了Rust表达式导向的设计哲学。Rust的函数设计简洁而强大,支持显式和隐式返回值、函数指针、闭包等高级特性。

本文将全面介绍Rust的函数系统,包括:

  • 函数的基础语法和定义
  • 函数参数的类型和传递方式
  • 返回值的显式和隐式写法
  • 函数作为值(函数指针)
  • 闭包基础
  • 函数的最佳实践

通过本文的学习,你将能够:

  • 熟练定义和调用Rust函数
  • 理解参数传递机制
  • 掌握返回值的使用
  • 使用函数组织代码
  • 编写清晰、可维护的函数

1. 函数基础

1.1 什么是函数?

函数是一段执行特定任务的代码块,可以接受输入(参数),执行操作,并返回结果。

函数的好处

  • 代码复用:避免重复编写相同代码
  • 模块化:将复杂问题分解为小函数
  • 可读性:函数名可以表达代码意图
  • 测试性:函数便于单元测试
  • 维护性:修改功能只需修改函数

1.2 基本函数定义

Rust使用 fn 关键字定义函数:

fn main() {
    println!("Hello, World!");
}

// 定义一个函数
fn greet() {
    println!("你好,Rust!");
}

fn main() {
    greet();  // 调用函数
}

语法结构

fn 函数名(参数列表) -> 返回类型 {
    // 函数体
}

关键特点

  • fn 关键字声明函数
  • 函数名使用蛇形命名(snake_case)
  • 参数列表在括号内(可为空)
  • 返回类型使用 -> 指定(可选)
  • 函数体用花括号包围

2. 函数参数

2.1 带参数的函数

函数可以接受参数,参数必须明确指定类型:

// 单个参数
fn greet(name: &str) {
    println!("你好,{}!", name);
}

// 多个参数
fn add(x: i32, y: i32) {
    println!("{} + {} = {}", x, y, x + y);
}

fn main() {
    greet("Alice");
    greet("Bob");
    
    add(10, 20);
    add(5, 3);
}

在这里插入图片描述

2.2 参数类型注解

Rust要求所有参数必须明确指定类型:

// ✅ 正确:明确指定类型
fn add(x: i32, y: i32) {
    println!("{} + {} = {}", x, y, x + y);
}

// ❌ 错误:缺少类型注解
// fn add(x, y) {  // 编译错误!
//     println!("{} + {} = {}", x, y, x + y);
// }

fn main() {
    add(10, 20);
}

2.3 不同参数类型示例

// 整数参数
fn double(x: i32) -> i32 {
    x * 2
}

// 浮点数参数
fn calculate_area(radius: f64) -> f64 {
    3.14159 * radius * radius
}

// 布尔参数
fn check_permission(is_admin: bool) {
    if is_admin {
        println!("有管理员权限");
    } else {
        println!("普通用户");
    }
}

// 字符串参数
fn print_info(name: &str, age: u32) {
    println!("姓名: {}, 年龄: {}", name, age);
}

// 数组参数
fn sum_array(numbers: &[i32]) -> i32 {
    numbers.iter().sum()
}

fn main() {
    println!("{}", double(5));           // 10
    println!("{:.2}", calculate_area(5.0)); // 78.54
    check_permission(true);
    print_info("Alice", 25);
    println!("{}", sum_array(&[1, 2, 3, 4, 5])); // 15
}

2.4 可变参数

函数参数默认不可变,如果需要修改,使用 mut 关键字:

// 不可变参数(默认)
fn process_value(x: i32) {
    // x = 10;  // 错误!不能修改不可变参数
    println!("{}", x);
}

// 可变参数
fn increment(mut x: i32) {
    x += 1;
    println!("增加后: {}", x);
}

fn main() {
    let num = 5;
    increment(num);  // num不会被修改
    println!("原始值: {}", num);  // 仍然是5
}

注意:即使参数是 mut,也不会修改调用者的变量(因为参数是值的拷贝)。
在这里插入图片描述

3. 函数返回值

3.1 显式返回(return语句)

使用 return 关键字显式返回值:

fn add(x: i32, y: i32) -> i32 {
    return x + y;
}

fn check_positive(x: i32) -> bool {
    if x > 0 {
        return true;
    }
    return false;
}

fn main() {
    let result = add(10, 20);
    println!("{}", result);  // 30
    
    println!("{}", check_positive(5));   // true
    println!("{}", check_positive(-5));  // false
}

在这里插入图片描述

3.2 隐式返回(表达式返回值)

Rust函数最独特的特性:最后一行表达式自动作为返回值(没有分号):

// 方式1:显式return
fn add1(x: i32, y: i32) -> i32 {
    return x + y;
}

// 方式2:隐式返回(推荐)
fn add2(x: i32, y: i32) -> i32 {
    x + y  // 注意:没有分号!这是一个表达式
}

// 错误示例
fn add3(x: i32, y: i32) -> i32 {
    x + y;  // 有分号,变成了语句,返回 (),编译错误!
}

fn main() {
    println!("{}", add1(10, 20));
    println!("{}", add2(10, 20));
}

关键规则

  • 表达式(无分号)→ 返回值
  • 语句(有分号)→ 返回 ()

3.3 多返回值(使用元组)

Rust函数只能返回一个值,但可以使用元组返回多个值:

// 返回多个值(使用元组)
fn calculate(x: i32, y: i32) -> (i32, i32, i32, i32) {
    let sum = x + y;
    let diff = x - y;
    let prod = x * y;
    let quot = x / y;
    
    (sum, diff, prod, quot)  // 返回元组
}

// 使用元组解构
fn get_name_and_age() -> (&'static str, u32) {
    ("Alice", 25)
}

fn main() {
    let (sum, diff, prod, quot) = calculate(10, 3);
    println!("和: {}, 差: {}, 积: {}, 商: {}", sum, diff, prod, quot);
    
    let (name, age) = get_name_and_age();
    println!("姓名: {}, 年龄: {}", name, age);
}

3.4 早期返回

可以在函数中间使用 return 提前返回:

fn find_first_even(numbers: &[i32]) -> Option<i32> {
    for &num in numbers {
        if num % 2 == 0 {
            return Some(num);  // 早期返回
        }
    }
    None  // 隐式返回
}

fn divide(x: f64, y: f64) -> Result<f64, &'static str> {
    if y == 0.0 {
        return Err("除数不能为零");  // 早期返回错误
    }
    Ok(x / y)  // 正常返回
}

fn main() {
    let numbers = vec![1, 3, 5, 8, 9];
    match find_first_even(&numbers) {
        Some(n) => println!("找到第一个偶数: {}", n),
        None => println!("未找到偶数"),
    }
    
    match divide(10.0, 2.0) {
        Ok(result) => println!("结果: {}", result),
        Err(e) => println!("错误: {}", e),
    }
}

3.5 无返回值函数

如果函数不返回值,返回类型是 ()(单元类型),可以省略:

// 方式1:显式指定返回类型
fn print_hello() -> () {
    println!("Hello");
}

// 方式2:省略返回类型(推荐)
fn print_world() {
    println!("World");
}

fn main() {
    print_hello();
    print_world();
}

4. 函数调用

4.1 基本调用

fn greet(name: &str) {
    println!("你好,{}!", name);
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn main() {
    // 调用无返回值函数
    greet("Alice");
    
    // 调用有返回值函数
    let result = add(10, 20);
    println!("结果: {}", result);
    
    // 在表达式中调用
    println!("{} + {} = {}", 5, 3, add(5, 3));
}

4.2 函数调用的嵌套

函数可以在其他函数内部调用:

fn square(x: i32) -> i32 {
    x * x
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn calculate(x: i32, y: i32) -> i32 {
    let x_sq = square(x);     // 调用square
    let y_sq = square(y);     // 调用square
    add(x_sq, y_sq)           // 调用add
}

fn main() {
    let result = calculate(3, 4);
    println!("3² + 4² = {}", result);  // 25
}

4.3 递归调用

函数可以调用自身(递归):

// 计算阶乘
fn factorial(n: u64) -> u64 {
    if n <= 1 {
        1
    } else {
        n * factorial(n - 1)  // 递归调用
    }
}

// 斐波那契数列
fn fibonacci(n: u32) -> u64 {
    if n <= 1 {
        n as u64
    } else {
        fibonacci(n - 1) + fibonacci(n - 2)  // 递归调用
    }
}

fn main() {
    println!("5! = {}", factorial(5));      // 120
    println!("fib(10) = {}", fibonacci(10)); // 55
}

5. 函数的作用域和可见性

5.1 函数定义的位置

函数可以在模块的任何位置定义,调用顺序不受定义顺序影响:

fn main() {
    // 可以调用后面定义的函数
    greet();
    add(10, 20);
}

// 函数定义在main之后也可以
fn greet() {
    println!("Hello");
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

5.2 嵌套函数

Rust支持在函数内部定义函数(嵌套函数):

fn outer_function() {
    println!("外部函数");
    
    // 嵌套函数
    fn inner_function() {
        println!("内部函数");
    }
    
    inner_function();  // 只能在outer_function内部调用
}

fn main() {
    outer_function();
    // inner_function();  // 错误!无法在此处调用
}

5.3 函数可见性(pub关键字)

默认情况下,函数是私有的。使用 pub 关键字使其公开:

// 私有函数(默认)
fn private_func() {
    println!("私有函数");
}

// 公开函数
pub fn public_func() {
    println!("公开函数");
}

fn main() {
    // 在同一个模块内,都可以调用
    private_func();
    public_func();
}

6. 函数作为值

6.1 函数指针

Rust中函数也是一等公民,可以作为值传递:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn multiply(x: i32, y: i32) -> i32 {
    x * y
}

fn main() {
    // 函数指针(函数类型)
    let op: fn(i32, i32) -> i32 = add;
    
    // 通过函数指针调用
    let result = op(10, 20);
    println!("{}", result);  // 30
    
    // 可以重新赋值
    let op = multiply;
    println!("{}", op(10, 20));  // 200
}

6.2 函数作为参数

函数可以作为参数传递给其他函数:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn multiply(x: i32, y: i32) -> i32 {
    x * y
}

// 接受函数作为参数
fn calculate(op: fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
    op(x, y)
}

fn main() {
    let result1 = calculate(add, 10, 20);
    let result2 = calculate(multiply, 10, 20);
    
    println!("add(10, 20) = {}", result1);      // 30
    println!("multiply(10, 20) = {}", result2); // 200
}

6.3 高阶函数示例

// 对每个元素应用函数
fn map_array<F>(arr: &[i32], func: F) -> Vec<i32>
where
    F: Fn(i32) -> i32,
{
    arr.iter().map(|&x| func(x)).collect()
}

fn double(x: i32) -> i32 {
    x * 2
}

fn square(x: i32) -> i32 {
    x * x
}

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    
    // 使用函数指针
    let doubled = map_array(&numbers, double);
    println!("翻倍: {:?}", doubled);  // [2, 4, 6, 8, 10]
    
    // 使用函数指针
    let squared = map_array(&numbers, square);
    println!("平方: {:?}", squared);  // [1, 4, 9, 16, 25]
}

7. 闭包基础

7.1 什么是闭包?

闭包是可以捕获其环境变量的匿名函数:

fn main() {
    // 普通函数
    fn add_one(x: i32) -> i32 {
        x + 1
    }
    
    // 闭包(捕获环境变量)
    let base = 10;
    let add_base = |x| x + base;  // 捕获了base变量
    
    println!("{}", add_one(5));      // 6
    println!("{}", add_base(5));     // 15
}

7.2 闭包语法

fn main() {
    // 闭包语法:|参数| -> 返回类型 { 体 }
    
    // 最简单:单行表达式
    let add = |x, y| x + y;
    
    // 指定类型
    let add_typed = |x: i32, y: i32| -> i32 {
        x + y
    };
    
    // 多行闭包
    let complex = |x: i32, y: i32| -> i32 {
        let sum = x + y;
        let product = x * y;
        sum + product
    };
    
    println!("{}", add(10, 20));           // 30
    println!("{}", add_typed(10, 20));      // 30
    println!("{}", complex(3, 4));         // 19
}

7.3 闭包与函数的区别

特性 函数 闭包
语法 fn name() {} |x| x + 1
捕获环境 ❌ 不能 ✅ 可以
类型推断 必须显式类型 可以推断
性能 总是静态分发 可以静态或动态分发
fn main() {
    let multiplier = 3;
    
    // 闭包可以捕获环境变量
    let closure = |x| x * multiplier;
    
    // 函数不能捕获,必须通过参数传递
    fn function(x: i32, mul: i32) -> i32 {
        x * mul
    }
    
    println!("闭包: {}", closure(10));         // 30
    println!("函数: {}", function(10, 3));     // 30
}

8. 实战示例

示例1:数学函数库

// 数学工具函数
mod math {
    pub fn add(x: f64, y: f64) -> f64 {
        x + y
    }
    
    pub fn subtract(x: f64, y: f64) -> f64 {
        x - y
    }
    
    pub fn multiply(x: f64, y: f64) -> f64 {
        x * y
    }
    
    pub fn divide(x: f64, y: f64) -> Option<f64> {
        if y != 0.0 {
            Some(x / y)
        } else {
            None
        }
    }
    
    pub fn power(base: f64, exp: i32) -> f64 {
        let mut result = 1.0;
        for _ in 0..exp.abs() {
            result *= base;
        }
        if exp < 0 {
            1.0 / result
        } else {
            result
        }
    }
    
    pub fn abs(x: f64) -> f64 {
        if x >= 0.0 {
            x
        } else {
            -x
        }
    }
}

fn main() {
    println!("10 + 5 = {}", math::add(10.0, 5.0));
    println!("10 - 5 = {}", math::subtract(10.0, 5.0));
    println!("10 * 5 = {}", math::multiply(10.0, 5.0));
    
    match math::divide(10.0, 5.0) {
        Some(result) => println!("10 / 5 = {}", result),
        None => println!("除零错误"),
    }
    
    println!("2^3 = {}", math::power(2.0, 3));
    println!("|-5| = {}", math::abs(-5.0));
}

示例2:字符串处理工具

fn reverse_string(s: &str) -> String {
    s.chars().rev().collect()
}

fn count_words(s: &str) -> usize {
    s.split_whitespace().count()
}

fn capitalize_first(s: &str) -> String {
    if s.is_empty() {
        return String::new();
    }
    let mut chars: Vec<char> = s.chars().collect();
    chars[0] = chars[0].to_uppercase().next().unwrap();
    chars.into_iter().collect()
}

fn is_palindrome(s: &str) -> bool {
    let normalized: String = s.chars()
        .filter(|c| c.is_alphanumeric())
        .map(|c| c.to_lowercase().next().unwrap())
        .collect();
    
    let reversed: String = normalized.chars().rev().collect();
    normalized == reversed
}

fn main() {
    let text = "Hello World";
    
    println!("原文: {}", text);
    println!("反转: {}", reverse_string(text));
    println!("单词数: {}", count_words(text));
    println!("首字母大写: {}", capitalize_first(text));
    
    println!("'racecar' 是回文: {}", is_palindrome("racecar"));
    println!("'hello' 是回文: {}", is_palindrome("hello"));
}

示例3:温度转换器

fn celsius_to_fahrenheit(c: f64) -> f64 {
    c * 9.0 / 5.0 + 32.0
}

fn fahrenheit_to_celsius(f: f64) -> f64 {
    (f - 32.0) * 5.0 / 9.0
}

fn celsius_to_kelvin(c: f64) -> f64 {
    c + 273.15
}

fn convert_temperature(value: f64, from: &str, to: &str) -> Option<f64> {
    match (from, to) {
        ("C", "F") => Some(celsius_to_fahrenheit(value)),
        ("F", "C") => Some(fahrenheit_to_celsius(value)),
        ("C", "K") => Some(celsius_to_kelvin(value)),
        ("K", "C") => Some(value - 273.15),
        ("F", "K") => {
            let c = fahrenheit_to_celsius(value);
            Some(celsius_to_kelvin(c))
        }
        ("K", "F") => {
            let c = value - 273.15;
            Some(celsius_to_fahrenheit(c))
        }
        _ => None,
    }
}

fn main() {
    let temp_c = 25.0;
    
    println!("{}°C = {:.1}°F", temp_c, celsius_to_fahrenheit(temp_c));
    println!("{}°C = {:.2}K", temp_c, celsius_to_kelvin(temp_c));
    
    match convert_temperature(100.0, "F", "C") {
        Some(temp) => println!("100°F = {:.1}°C", temp),
        None => println!("不支持的转换"),
    }
}

示例4:数据验证函数

fn is_valid_email(email: &str) -> bool {
    email.contains('@') && email.contains('.')
}

fn is_valid_age(age: u32) -> bool {
    age > 0 && age < 150
}

fn is_strong_password(password: &str) -> bool {
    password.len() >= 8
        && password.chars().any(|c| c.is_uppercase())
        && password.chars().any(|c| c.is_lowercase())
        && password.chars().any(|c| c.is_numeric())
}

fn validate_user(name: &str, age: u32, email: &str, password: &str) -> (bool, Vec<&'static str>) {
    let mut errors = Vec::new();
    
    if name.is_empty() {
        errors.push("姓名不能为空");
    }
    
    if !is_valid_age(age) {
        errors.push("年龄必须在1-149之间");
    }
    
    if !is_valid_email(email) {
        errors.push("邮箱格式无效");
    }
    
    if !is_strong_password(password) {
        errors.push("密码必须至少8位,包含大小写字母和数字");
    }
    
    let is_valid = errors.is_empty();
    (is_valid, errors)
}

fn main() {
    let (valid, errors) = validate_user("Alice", 25, "alice@example.com", "Password123");
    
    if valid {
        println!("验证通过!");
    } else {
        println!("验证失败:");
        for error in errors {
            println!("  - {}", error);
        }
    }
}

9. 常见错误与解决方案

错误1:缺少返回类型

// 错误:想返回值但没有指定返回类型
// fn add(x: i32, y: i32) {  // 返回类型是 ()
//     x + y  // 错误!不能返回值
// }

// 正确:指定返回类型
fn add(x: i32, y: i32) -> i32 {
    x + y  // 正确!
}

错误2:分号导致的返回值问题

// 错误:表达式末尾加了分号
// fn get_value() -> i32 {
//     42;  // 这是语句,返回 ()
// }

// 正确:没有分号
fn get_value() -> i32 {
    42  // 这是表达式,返回值
}

fn main() {
    println!("{}", get_value());
}

错误3:参数类型缺失

// 错误:参数没有类型
// fn add(x, y) {  // 编译错误!
//     x + y
// }

// 正确:所有参数必须有类型
fn add(x: i32, y: i32) -> i32 {
    x + y
}

错误4:函数名冲突

// 错误:函数名重复
// fn calculate(x: i32) -> i32 { x }
// fn calculate(x: f64) -> f64 { x }  // Rust不支持函数重载

// 正确:使用不同的函数名
fn calculate_i32(x: i32) -> i32 { x }
fn calculate_f64(x: f64) -> f64 { x }

// 或者使用泛型(后续学习)

10. 最佳实践

10.1 函数命名

// ✅ 好的命名:清晰表达意图
fn calculate_total_price(items: &[Item]) -> f64 { }
fn is_valid_email(email: &str) -> bool { }
fn get_user_by_id(id: u32) -> Option<User> { }

// ❌ 不好的命名
fn calc(x: &[Item]) -> f64 { }      // 太简短
fn check(s: &str) -> bool { }        // 不够描述性
fn get(id: u32) -> Option<User> { }  // 不够具体

10.2 函数长度

  • 保持函数简短:一个函数做一件事
  • 20-30行以内:如果超过,考虑拆分
  • 单一职责:每个函数只有一个明确的目的

10.3 参数设计

// ✅ 好的参数设计
fn process_user_data(name: &str, age: u32, email: &str) { }

// ❌ 参数过多
// fn process(name: &str, age: u32, email: &str, phone: &str, 
//           address: &str, city: &str, country: &str) { }
// 建议:使用结构体

10.4 返回值设计

// ✅ 好的返回值设计
fn find_user(id: u32) -> Option<User> { }  // 可能找不到
fn divide(x: f64, y: f64) -> Result<f64, String> { }  // 可能出错

// ❌ 使用特殊值表示错误(不推荐)
// fn find_user(id: u32) -> User { }  // 找不到时怎么办?

11. 扩展练习

练习1:基本函数

编写函数计算圆的面积、周长,以及矩形的面积和周长。

练习2:字符串处理

编写函数实现字符串反转、去空格、统计字符等功能。

练习3:数学函数

实现计算平均值、最大值、最小值的函数。

练习4:验证函数

编写验证电话号码、身份证号格式的函数(简化版)。

练习5:递归函数

使用递归实现字符串反转、计算最大公约数。

练习6:函数组合

编写多个小函数,组合使用完成复杂任务。

12. 总结

核心要点回顾

  1. 函数定义:使用 fn 关键字,必须指定参数类型
  2. 返回值:使用 -> 指定返回类型,表达式自动返回
  3. 参数:必须明确类型,可以是可变(mut
  4. 函数调用:按函数名和参数调用
  5. 函数指针:函数可以作为值传递
  6. 闭包:可以捕获环境的匿名函数

关键特性

  • 表达式导向:最后一行表达式自动返回
  • 类型安全:所有参数和返回值必须有明确类型
  • 一等公民:函数可以作为值传递
  • 简洁语法:支持隐式返回,代码更简洁
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐