面试题答案
一键面试语法差异
- 闭包:闭包使用
|参数| {代码块}
的语法定义,参数可以省略类型标注,代码块可以是表达式或语句块。例如:let closure = |x| x * 2;
- 泛型函数:泛型函数在函数名后使用
<类型参数>
声明类型参数,参数必须明确标注类型。例如:fn generic_function<T>(arg: T) -> T { arg }
类型推断差异
- 闭包:闭包的类型推断非常灵活,编译器可以根据上下文推断出闭包参数和返回值的类型。例如:
let num = 5; let result = (|x| x * 2)(num);
编译器能推断出闭包参数x
为i32
类型。 - 泛型函数:泛型函数的类型参数需要在调用时明确指定或者通过函数签名和上下文进行推断。例如:
let result = generic_function::<i32>(5);
或let num: i32 = 5; let result = generic_function(num);
捕获环境变量差异
- 闭包:闭包可以捕获其定义所在环境中的变量,捕获方式有三种:按值捕获(
move
)、按可变引用捕获(mut
)和按不可变引用捕获。例如:
let x = 5;
let closure = move || x * 2;
这里闭包按值捕获了 x
。
2. 泛型函数:泛型函数不能捕获环境变量,它只操作传入的参数。
优先选择闭包的场景
- 作为参数传递给其他函数:当需要将一段逻辑作为参数传递给另一个函数时,闭包非常方便。例如
Iterator
trait 中的filter
方法,接受一个闭包来过滤元素:
let numbers = vec![1, 2, 3, 4, 5];
let even_numbers: Vec<i32> = numbers.iter().filter(|&x| x % 2 == 0).cloned().collect();
- 需要捕获环境变量:当一段逻辑需要依赖外部环境中的变量时,闭包是首选。例如,在实现一个计数器:
let mut counter = 0;
let increment = || {
counter += 1;
counter
};
let result1 = increment();
let result2 = increment();
优先选择泛型函数的场景
- 通用算法实现:当实现一个通用的算法,不依赖于外部环境,只对不同类型数据进行相同操作时,泛型函数更合适。例如排序函数:
fn sort<T: Ord>(vec: &mut Vec<T>) {
vec.sort();
}
let mut numbers = vec![3, 1, 2];
sort(&mut numbers);
- 提高代码复用性:当多个地方需要使用相同逻辑但针对不同类型时,泛型函数可以避免重复代码。例如实现一个获取最大值的函数:
fn max<T: Ord>(a: T, b: T) -> T {
if a > b { a } else { b }
}
let num_max = max(5, 3);
let str_max = max("apple".to_string(), "banana".to_string());