函数的参数需要标注类型。
函数最后的表达式将作为返回值。也可在函数内使用 return 语句来提前返回值。

  1. // 和 C/C++ 不一样,Rust 的函数定义位置是没有限制的
  2. fn main() {
  3. // 我们可以在这里使用函数,后面再定义它
  4. fizzbuzz(100);
  5. }
  6. // 一个 “不” 返回值的函数。实际上会返回一个单元类型 `()`。
  7. fn fizzbuzz(n: u32) -> () {
  8. println!("fizzbuzz");
  9. }
  10. // 当函数返回 `()` 时,函数签名可以省略返回类型

方法

方法(method)是依附于对象的函数。这些方法通过关键字 self 来访问对象中的数据和其他。方法在 impl 代码块中定义。

静态方法

  1. struct Point {
  2. x: f64,
  3. y: f64,
  4. }
  5. // 实现的代码块,`Point` 的所有方法都在这里给出
  6. impl Point {
  7. // 这是一个静态方法(static method)
  8. // 静态方法不需要被实例调用
  9. // 这类方法一般用作构造器(constructor)
  10. fn origin() -> Point {
  11. Point { x: 0.0, y: 0.0 }
  12. }
  13. // 另外一个静态方法,需要两个参数:
  14. fn new(x: f64, y: f64) -> Point {
  15. Point { x: x, y: y }
  16. }
  17. }

实例方法

  1. struct Rectangle {
  2. p1: Point,
  3. p2: Point,
  4. }
  5. impl Rectangle {
  6. // 这是一个实例方法(instance method)
  7. // `&self` 是 `self: &Self` 的语法糖(sugar),其中 `Self` 是方法调用者的
  8. // 类型。在这个例子中 `Self` = `Rectangle`
  9. fn area(&self) -> f64 {
  10. // `self` 通过点运算符来访问结构体字段
  11. let Point { x: x1, y: y1 } = self.p1;
  12. let Point { x: x2, y: y2 } = self.p2;
  13. // `abs` 是一个 `f64` 类型的方法,返回调用者的绝对值
  14. ((x1 - x2) * (y1 - y2)).abs()
  15. }
  16. fn perimeter(&self) -> f64 {
  17. let Point { x: x1, y: y1 } = self.p1;
  18. let Point { x: x2, y: y2 } = self.p2;
  19. 2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
  20. }
  21. // 这个方法要求调用者是可变的
  22. // `&mut self` 为 `self: &mut Self` 的语法糖
  23. fn translate(&mut self, x: f64, y: f64) {
  24. }
  25. }

非引用的实例方法

  1. // `Pair` 拥有资源:两个堆分配的整型
  2. struct Pair(Box<i32>, Box<i32>);
  3. impl Pair {
  4. // 这个方法会 “消耗” 调用者的资源
  5. // `self` 为 `self: Self` 的语法糖
  6. fn destroy(self) {
  7. // 解构 `self`
  8. let Pair(first, second) = self;
  9. println!("Destroying Pair({}, {})", first, second);
  10. // `first` 和 `second` 离开作用域后释放
  11. }
  12. }

闭包

闭包(closure),也叫 lambda,是一类能够捕获周围作用中变量的函数。调用闭包时,输入和返回类型两者都可以自动推导,而输入变量名必须指明。
特点:

  • 声明时使用 || 替代 () 将输入参数括起来。
  • 函数体定界符({})对于单个表达式是可选的,其他情况必须加上。
  • 有能力捕获外部环境的变量。 ``rust fn main() { // 闭包是匿名的,这里我们将它们绑定到引用。 // 类型标注和函数的一样,不过类型标注和使用{}` 来围住函数体都是可选的。 // 这些匿名函数(nameless function)被赋值给合适地命名的变量。 let closure_annotated = |i: i32| -> i32 { i + 1 }; let closure_inferred = |i | i + 1 ;

    // 译注:将闭包绑定到引用的说法可能不准。 // 据语言参考 // 闭包表达式产生的类型就是 “闭包类型”,不属于引用类型,而且确实无法对上面两个 // closure_xxx 变量解引用。

    let i = 1; // 调用闭包。 println!(“closure_annotated: {}”, closure_annotated(i)); println!(“closure_inferred: {}”, closure_inferred(i));

    // 没有参数的闭包,返回一个 i32 类型。 // 返回类型是自动推导的。 let one = || 1; println!(“closure returning one: {}”, one()); }

  1. <a name="wF3dh"></a>
  2. ### 捕获
  3. 闭包中变量捕获(capture)很灵活,既可移动(move),又可借用(borrow)变量。<br />可以通过以下手段捕获变量:
  4. - 通过引用: `&T`
  5. - 通过可变引用: `&mut T`
  6. - 通过值: `T`
  7. 闭包更倾向于通过引用来捕获变量,并且只在被要求时才使用其他手段。
  8. ```rust
  9. fn main() {
  10. use std::mem;
  11. let color = "green";
  12. // 这个闭包打印 `color`。它会立即借用(通过引用,`&`)`color` 并将该借用和
  13. // 闭包本身存储到 `print` 变量中。`color` 会一直保持被借用状态直到
  14. // `print` 离开作用域。
  15. // `println!` 只需传引用就能使用,而这个闭包捕获的也是变量的引用,因此无需
  16. // 进一步处理就可以使用 `println!`。
  17. let print = || println!("`color`: {}", color);
  18. // 调用闭包,闭包又借用 `color`。
  19. print();
  20. print();
  21. let mut count = 0;
  22. // 这个闭包使 `count` 值增加。要做到这点,它需要得到 `&mut count` 或者
  23. // `count` 本身,但 `&mut count` 的要求没那么严格,所以我们采取这种方式。
  24. // 该闭包立即借用 `count`。
  25. //
  26. // `inc` 前面需要加上 `mut`,因为闭包里存储着一个 `&mut` 变量。调用闭包时,
  27. // 该变量的变化就意味着闭包内部发生了变化。因此闭包需要是可变的。
  28. let mut inc = || {
  29. count += 1;
  30. println!("`count`: {}", count);
  31. };
  32. // 不可复制类型(non-copy type)。
  33. let movable = Box::new(3);
  34. // `mem::drop` 要求 `T` 类型本身,所以闭包将会捕获变量的值。这种情况下,
  35. // 可复制类型将会复制给闭包,从而原始值不受影响。不可复制类型必须移动
  36. // (move)到闭包中,因而 `movable` 变量在这里立即移动到了闭包中。
  37. let consume = || {
  38. println!("`movable`: {:?}", movable);
  39. mem::drop(movable);
  40. };
  41. // `consume` 消耗了该变量,所以该闭包只能调用一次。
  42. consume();
  43. }

在竖线 | 之前使用 move 会强制闭包取得被捕获变量的所有权:

  1. fn main() {
  2. // `Vec` 在语义上是不可复制的。
  3. let haystack = vec![1, 2, 3];
  4. let contains = move |needle| haystack.contains(needle);
  5. println!("{}", contains(&1));
  6. //println!("There're {} elements in vec", haystack.len());
  7. // ^ 取消上面一行的注释将导致编译时错误,因为借用检查不允许在变量被移动走
  8. // 之后继续使用它。
  9. // 在闭包的签名中删除 `move` 会导致闭包以不可变方式借用 `haystack`,因此之后
  10. // `haystack` 仍然可用,取消上面的注释也不会导致错误。
  11. }

作为输入参数

当以闭包作为输入参数时,必须指出闭包的完整类型,它是通过使用以下 trait 中的一种来指定的。其受限制程度按以下顺序递减:

  • Fn 表示捕获方式为通过引用(&T)的闭包
  • FnMut 表示捕获方式为通过可变引用( &mut T)的闭包
  • FnOnce 表示捕获方式为通过值( T )的闭包

    顺序之所以是这样,是因为 &T 只是获取了不可变的引用,&mut T 则可以改变 变量,T 则是拿到了变量的所有权而非借用。

事实上是在满足使用需求的前提下尽量以限制最多的方式捕获。

如果一个类型说明为 FnOnce 的闭包作为参数。这说明闭包可能采取 &T&mut TT 中的一种捕获方式;
如果参数的类型说明是 Fn,那么不允许该闭包通过 &mut TT 捕获变量。

  1. // 该函数将闭包作为参数并调用它。
  2. fn apply<F>(f: F) where
  3. // 闭包没有输入值和返回值。
  4. F: FnOnce() {
  5. // ^ 试一试:将 `FnOnce` 换成 `Fn` 或 `FnMut`。
  6. f();
  7. }
  8. // 输入闭包,返回一个 `i32` 整型的函数。
  9. fn apply_to_3<F>(f: F) -> i32 where
  10. // 闭包处理一个 `i32` 整型并返回一个 `i32` 整型。
  11. F: Fn(i32) -> i32 {
  12. f(3)
  13. }
  14. fn main() {
  15. use std::mem;
  16. let greeting = "hello";
  17. // 不可复制的类型。
  18. // `to_owned` 从借用的数据创建有所有权的数据。
  19. let mut farewell = "goodbye".to_owned();
  20. // 捕获 2 个变量:通过引用捕获 `greeting`,通过值捕获 `farewell`。
  21. let diary = || {
  22. // `greeting` 通过引用捕获,故需要闭包是 `Fn`。
  23. println!("I said {}.", greeting);
  24. // 下文改变了 `farewell` ,因而要求闭包通过可变引用来捕获它。
  25. // 现在需要 `FnMut`。
  26. farewell.push_str("!!!");
  27. println!("Then I screamed {}.", farewell);
  28. println!("Now I can sleep. zzzzz");
  29. // 手动调用 drop 又要求闭包通过值获取 `farewell`。
  30. // 现在需要 `FnOnce`。
  31. mem::drop(farewell);
  32. };
  33. // 以闭包作为参数,调用函数 `apply`。
  34. apply(diary);
  35. // 闭包 `double` 满足 `apply_to_3` 的 trait 约束。
  36. let double = |x| 2 * x;
  37. println!("3 doubled: {}", apply_to_3(double));
  38. }

类型匿名

使用闭包作为函数参数,这要求闭包是泛型的。

  1. // `F` 必须是泛型的。
  2. fn apply<F>(f: F) where
  3. F: FnOnce() {
  4. f();
  5. }

指明为该结构体实现的是 FnFnMut、或 FnOnce 中的哪种 trait,对于约束该结构体的类型而言就已经足够了。

  1. // `F` 必须为一个没有输入参数和返回值的闭包实现 `Fn`,这和对 `print` 的
  2. // 要求恰好一样。
  3. fn apply<F>(f: F) where
  4. F: Fn() {
  5. f();
  6. }
  7. fn main() {
  8. let x = 7;
  9. // 捕获 `x` 到匿名类型中,并为它实现 `Fn`。
  10. // 将闭包存储到 `print` 中。
  11. let print = || println!("{}", x);
  12. apply(print);
  13. }

输入函数

如果你声明一个接受闭包作为参数的函数,那么任何满足该闭包的 trait 约束的函数都可以作为其参数。

  1. // 定义一个函数,可以接受一个由 `Fn` 限定的泛型 `F` 参数并调用它。
  2. fn call_me<F: Fn()>(f: F) {
  3. f()
  4. }
  5. // 定义一个满足 `Fn` 约束的封装函数(wrapper function)。
  6. fn function() {
  7. println!("I'm a function!");
  8. }
  9. fn main() {
  10. // 定义一个满足 `Fn` 约束的闭包。
  11. let closure = || println!("I'm a closure!");
  12. call_me(closure);
  13. call_me(function);
  14. }

FnFnMutFnOnce 这些 trait 明确了闭包如何从周围的作用域中捕获变量。

作为输出参数

目前 Rust 只支持返回具体(非泛型)的类型。匿名闭包的类型是未知的,所以只有使用 impl Trait 才能返回一个闭包。
返回值的合法 trait:

  • Fn:和前面的一样
  • FnMut:和前面的一样
  • FnOnce:不平常的事情正是发生在这里。总之现在你需要返回 [FnBox][fnbox] 类型,目前该类型还是不稳定的。这个情况估计将来会改进。

除此之外,还必须使用 move 关键字,它表明所有的捕获都是通过值进行的。

  1. fn create_fn() -> impl Fn() {
  2. let text = "Fn".to_owned();
  3. move || println!("This is a: {}", text)
  4. }
  5. fn create_fnmut() -> impl FnMut() {
  6. let text = "FnMut".to_owned();
  7. move || println!("This is a: {}", text)
  8. }
  9. fn main() {
  10. let fn_plain = create_fn();
  11. let mut fn_mut = create_fnmut();
  12. fn_plain();
  13. fn_mut();
  14. }

std 中的例子

Iterator::any

Iterator::any 是一个函数,若传给它一个迭代器(iterator),当其中任一元素满足谓词(predicate)时它将返回 true,否则返回 false

谓词是闭包规定的, true/false 是闭包作用在元素上的返回值。

签名:

  1. pub trait Iterator {
  2. // 被迭代的类型。
  3. type Item;
  4. // `any` 接受 `&mut self` 参数(译注:回想一下,这是 `self: &mut Self` 的简写)
  5. // 表明函数的调用者可以被借用和修改,但不会被消耗。
  6. fn any<F>(&mut self, f: F) -> bool where
  7. // `FnMut` 表示被捕获的变量最多只能被修改,而不能被消耗。
  8. // `Self::Item` 指明了被捕获变量的类型(译注:是迭代器的元素本身的类型)
  9. F: FnMut(Self::Item) -> bool {}
  10. // 译注:原文说 `Self::Item` 表明变量是通过值传递给闭包的,这是说错了。
  11. // `FnMut` 就表示闭包只能通过引用捕获变量。把类型为 `T` 的变量作为闭包
  12. // 的参数不代表闭包会拿走它的值,也可能是拿走它的引用。
  13. }
  1. fn main() {
  2. let vec1 = vec![1, 2, 3];
  3. let vec2 = vec![4, 5, 6];
  4. // 对 vec 的 `iter()` 举出 `&i32`。(通过用 `&x` 匹配)把它解构成 `i32`。
  5. // 译注:注意 `any` 方法会自动地把 `vec.iter()` 举出的迭代器的元素一个个地
  6. // 传给闭包。因此闭包接收到的参数是 `&i32` 类型的。
  7. println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
  8. // 对 vec 的 `into_iter()` 举出 `i32` 类型。无需解构。
  9. println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
  10. let array1 = [1, 2, 3];
  11. let array2 = [4, 5, 6];
  12. // 对数组的 `iter()` 举出 `&i32`。
  13. println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
  14. // 对数组的 `into_iter()` 通常举出 `&i32`。
  15. println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
  16. }

Iterator::find

Iterator::find 是一个函数,在传给它一个迭代器时,将用 Option 类型返回第一个满足谓词的元素。

  1. pub trait Iterator {
  2. // 被迭代的类型。
  3. type Item;
  4. // `find` 接受 `&mut self` 参数,表明函数的调用者可以被借用和修改,
  5. // 但不会被消耗。
  6. fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where
  7. // `FnMut` 表示被捕获的变量最多只能被修改,而不能被消耗。
  8. // `&Self::Item` 指明了被捕获变量的类型(译注:是对迭代器元素的引用类型)
  9. P: FnMut(&Self::Item) -> bool {}
  10. }
  1. fn main() {
  2. let vec1 = vec![1, 2, 3];
  3. let vec2 = vec![4, 5, 6];
  4. // 对 vec1 的 `iter()` 举出 `&i32` 类型。
  5. let mut iter = vec1.iter();
  6. // 对 vec2 的 `into_iter()` 举出 `i32` 类型。
  7. let mut into_iter = vec2.into_iter();
  8. // 对迭代器举出的元素的引用是 `&&i32` 类型。解构成 `i32` 类型。
  9. // 译注:注意 `find` 方法会把迭代器元素的引用传给闭包。迭代器元素自身
  10. // 是 `&i32` 类型,所以传给闭包的是 `&&i32` 类型。
  11. println!("Find 2 in vec1: {:?}", iter .find(|&&x| x == 2));
  12. // 对迭代器举出的元素的引用是 `&i32` 类型。解构成 `i32` 类型。
  13. println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));
  14. let array1 = [1, 2, 3];
  15. let array2 = [4, 5, 6];
  16. // 对数组的 `iter()` 举出 `&i32`。
  17. println!("Find 2 in array1: {:?}", array1.iter() .find(|&&x| x == 2));
  18. // 对数组的 `into_iter()` 通常举出 `&i32``。
  19. println!("Find 2 in array2: {:?}", array2.into_iter().find(|&&x| x == 2));
  20. }

高阶函数

Rust 提供了高阶函数(Higher Order Function, HOF),指那些输入一个或多个函数,并且/或者产生一个更有用的函数的函数。
HOF 和惰性迭代器(lazy iterator)给 Rust 带来了函数式(functional)编程的风格。

  1. fn is_odd(n: u32) -> bool {
  2. n % 2 == 1
  3. }
  4. fn main() {
  5. println!("Find the sum of all the squared odd numbers under 1000");
  6. let upper = 1000;
  7. // 命令式(imperative)的写法
  8. // 声明累加器变量
  9. let mut acc = 0;
  10. // 迭代:0,1, 2, ... 到无穷大
  11. for n in 0.. {
  12. // 数字的平方
  13. let n_squared = n * n;
  14. if n_squared >= upper {
  15. // 若大于上限则退出循环
  16. break;
  17. } else if is_odd(n_squared) {
  18. // 如果是奇数就计数
  19. acc += n_squared;
  20. }
  21. }
  22. println!("imperative style: {}", acc);
  23. // 函数式的写法
  24. let sum_of_squared_odd_numbers: u32 =
  25. (0..).map(|n| n * n) // 所有自然数取平方
  26. .take_while(|&n| n < upper) // 取小于上限的
  27. .filter(|&n| is_odd(n)) // 取奇数
  28. .fold(0, |sum, i| sum + i); // 最后加起来
  29. println!("functional style: {}", sum_of_squared_odd_numbers);
  30. }

发散函数

发散函数(diverging function)绝不会返回。 它们使用 ! 标记,这是一个空类型。

  1. fn foo() -> ! {
  2. panic!("This call never returns.");
  3. }

和所有其他类型相反,这个类型无法实例化,因为此类型可能具有的所有可能值的集合为空。 注意,它与 () 类型不同,后者只有一个可能的值。

  1. fn main() {
  2. fn sum_odd_numbers(up_to: u32) -> u32 {
  3. let mut acc = 0;
  4. for i in 0..up_to {
  5. // 注意这个 match 表达式的返回值必须为 u32,
  6. // 因为 “addition” 变量是这个类型。
  7. let addition: u32 = match i%2 == 1 {
  8. // “i” 变量的类型为 u32,这毫无问题。
  9. true => i,
  10. // 另一方面,“continue” 表达式不返回 u32,但它仍然没有问题,
  11. // 因为它永远不会返回,因此不会违反匹配表达式的类型要求。
  12. false => continue,
  13. };
  14. acc += addition;
  15. }
  16. acc
  17. }
  18. println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
  19. }