今日目标
- 理解Rust函数的设计哲学 - 为什么Rust选择表达式导向而非语句导向?
- 掌握函数参数传递的所有权语义 - 函数签名是"安全契约"
- 深入理解表达式vs语句 - 这是Rust与其他语言的核心差异
- 实战函数式编程模式 - 高阶函数、函数作为参数/返回值
- 性能优化实践 - inline、单态化、零成本抽象
核心原理
1. 函数:所有权系统的"契约边界"
在Rust中,函数远不止是可重用的代码块。它是Rust所有权系统和借用检查器执行其安全保证的核心边界。每个函数签名都是一份由编译器严格执行的**"安全契约"**。
设计哲学:"编译期显式化"
与C++的"拷贝构造"或Java的"一切皆引用"不同,Rust的参数传递机制是其"移动语义"(Move Semantics)和"借用"(Borrowing)规则的直接体现。
2. 参数传递的三种模式
| 模式 | 语法 | 所有权语义 | 使用场景 |
|---|---|---|---|
| 移动 | T | 转移所有权 | 消费数据、构造者模式 |
| 不可变借用 | &T | 临时只读访问 | 读取数据、多个读者 |
| 可变借用 | &mut T | 临时独占访问 | 修改数据、单一写者 |
| 复制 | T: Copy | 按位复制 | 栈上小数据 |
3. 表达式 vs 语句:Rust的核心差异
语句 (Statement):不返回值,以分号结尾,如 let x = 5;
表达式 (Expression):返回值,无结尾分号,如 5 + 6 返回 11
为什么Rust选择表达式导向?
- 简洁性:减少
return关键字的使用 - 一致性:块、函数、if/match都是表达式,统一心智模型
- 函数式编程:便于链式调用和组合
- 类型安全:编译器能更好地推断返回类型
代码实例
实例1:函数基础与表达式返回值
fn main() {
let sum = add(5, 3);
println!("5 + 3 = {}", sum);
let value = {
let x = 10;
let y = 20;
x + y // 块表达式返回值
};
println!("块表达式值: {}", value);
}
fn add(a: i32, b: i32) -> i32 {
a + b // 自动返回,无需return
}
实例2:参数传递与所有权
fn main() {
// 移动语义
let s1 = String::from("Hello");
let s2 = take_ownership(s1);
// println!("{}", s1); // 错误!s1已移动
// 借用语义
let s3 = String::from("World");
let len = calculate_length(&s3);
println!("'{}'长度: {}", s3, len); // s3仍可用
// 可变借用
let mut s4 = String::from("Hello");
append_world(&mut s4);
}
fn take_ownership(s: String) -> String {
println!("获得: {}", s);
s
}
fn calculate_length(s: &String) -> usize {
s.len()
}
fn append_world(s: &mut String) {
s.push_str(" World");
}
实例3:高阶函数与函数指针
fn main() {
// 函数作为参数
let result = apply_operation(5, double);
println!("double(5) = {}", result);
let result = apply_operation(5, square);
println!("square(5) = {}", result);
// 函数作为返回值
let multiplier = create_multiplier(3);
println!("3 * 7 = {}", multiplier(7));
// 闭包作为参数
let numbers = vec![1, 2, 3, 4, 5];
let sum = reduce(&numbers, |acc, x| acc + x, 0);
println!("sum = {}", sum);
}
// 高阶函数:接收函数作为参数
fn apply_operation(x: i32, operation: fn(i32) -> i32) -> i32 {
operation(x)
}
fn double(x: i32) -> i32 { x * 2 }
fn square(x: i32) -> i32 { x * x }
// 返回函数的函数
fn create_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
// 泛型高阶函数
fn reduce<T>(items: &[T], operation: fn(T, T) -> T, initial: T) -> T
where
T: Copy,
{
let mut result = initial;
for &item in items {
result = operation(result, item);
}
result
}
实例4:泛型函数与Trait Bounds
fn main() {
let numbers = vec![3, 1, 4, 1, 5];
println!("最大值: {:?}", find_max(&numbers));
let floats = vec![3.14, 2.71, 1.41];
println!("最大值: {:?}", find_max(&floats));
}
// 泛型函数:查找最大值
fn find_max<T>(list: &[T]) -> Option<T>
where
T: PartialOrd + Copy,
{
if list.is_empty() {
return None;
}
let mut max = list[0];
for &item in list {
if item > max {
max = item;
}
}
Some(max)
}
最佳实践
1. API设计原则
// ✅ 优先使用引用
fn process(text: &str) // 接受字符串切片
// ❌ 避免不必要的所有权转移
fn process_bad(text: String) // 强制转移所有权
// ✅ 返回Result处理错误
fn parse_config(path: &str) -> Result<Config, ParseError>
// ✅ 使用impl Trait简化返回类型
fn create_iterator() -> impl Iterator<Item = i32>
2. 性能优化
// #[inline] - 提示编译器内联小函数
#[inline]
fn small_function(x: i32) -> i32 {
x * 2
}
// #[inline(always)] - 强制内联
#[inline(always)]
fn critical_path(x: i32) -> i32 {
x + 1
}
// 泛型单态化 - 编译时生成具体代码
fn generic_id<T>(x: T) -> T { x }
// 编译后生成: generic_id_i32, generic_id_f64等
常见陷阱
陷阱1:表达式后加分号
fn wrong_add(a: i32, b: i32) -> i32 {
a + b; // ❌ 分号使其成为语句,返回()
}
fn correct_add(a: i32, b: i32) -> i32 {
a + b // ✅ 表达式,返回i32
}
陷阱2:借用规则冲突
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变借用
let r2 = &s; // 可以多个不可变借用
// let r3 = &mut s; // ❌ 错误!不能同时有可变和不可变借用
println!("{} {}", r1, r2);
// r1, r2 在这里之后不再使用
let r3 = &mut s; // ✅ 现在可以了
}
练习题
练习1:实现通用过滤器
要求: 实现一个泛型过滤函数,接收切片和谓词函数,返回满足条件的元素。
参考答案
fn filter<T>(items: &[T], predicate: fn(&T) -> bool) -> Vec<T>
where
T: Clone,
{
items
.iter()
.filter(|&x| predicate(x))
.cloned()
.collect()
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens = filter(&numbers, |&x| x % 2 == 0);
println!("{:?}", evens); // [2, 4, 6]
}
练习2:实现函数组合
要求: 实现函数组合,将两个函数组合成一个新函数。
参考答案
fn compose<F, G, T, U, V>(f: F, g: G) -> impl Fn(T) -> V
where
F: Fn(U) -> V,
G: Fn(T) -> U,
{
move |x| f(g(x))
}
fn main() {
let add_one = |x: i32| x + 1;
let double = |x: i32| x * 2;
let add_then_double = compose(double, add_one);
println!("{}", add_then_double(5)); // 12
}
权威引用
官方文档
性能优化
自我评估
- 理解函数作为所有权契约边界的设计
- 掌握参数传递的三种模式
- 区分表达式和语句
- 能够编写高阶函数
- 理解泛型函数的单态化
学习记录: 2026-04-04 Day 3: 函数