提取函数消除重复

  1. fn main() {
  2. let number_list = vec![34, 50, 25, 100, 65];
  3. let mut largest = number_list[0];
  4. for number in number_list {
  5. if number > largest {
  6. largest = number;
  7. }
  8. }
  9. println!("The largest number is {}", largest);
  10. }

新增需求,增对第二个 vector 也挑出最大值

  1. fn main() {
  2. let number_list = vec![34, 50, 25, 100, 65];
  3. let mut largest = number_list[0];
  4. for number in number_list {
  5. if number > largest {
  6. largest = number;
  7. }
  8. }
  9. println!("The largest number is {}", largest);
  10. let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
  11. let mut largest = number_list[0];
  12. for number in number_list {
  13. if number > largest {
  14. largest = number;
  15. }
  16. }
  17. println!("The largest number is {}", largest);
  18. }

重复代码

  • 重复代码的危害
    • 容易出错
    • 需求变更时需要在多出进行修改
  • 消除重复:提取函数 ```rust fn largest(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list {
    1. if item > largest {
    2. largest = item;
    3. }
    } largest }

fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!(“The largest number is {}”, result);

  1. let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
  2. let result = largest(&number_list);
  3. println!("The largest number is {}", result);

}

  1. <a name="FmkmE"></a>
  2. ### 消除重复的步骤
  3. - 识别重复代码
  4. - 提取重复代码到函数体中,并在函数签名中指定函数的输入和返回值
  5. - 将重复的代码使用函数调用进行替代
  6. <a name="m7XOX"></a>
  7. ### 泛型
  8. - 泛型:提高代码**复用**能力
  9. - 处理重复代码的问题
  10. - 泛型是具体类型或其他属性的抽象代替
  11. - 编写的代码不是最终的代码,而是一种**模板**,里面有一些**占位符**
  12. - 编译器在**编译时**将“占位符”**替换为具体的类型**
  13. - 例如:`fn largest<`**`T`**`>(list: &[`**`T`**`]) -> `**`T`**` {...}`
  14. > 这个函数的定义使用了**泛型类型参数**,这个函数的定义其实就是一个模板,里面有一些占位符,
  15. > 这个大写的 T,就是占位符,写代码的时候 T ,它可以是任意的类型,但是在编译的过程中,
  16. > 编译器会根据你具体的使用,把 T 替换成具体的类型,这个过程叫做 **单态化**。
  17. - 类型参数:
  18. - 很短,通常一个字母
  19. - CamelCase
  20. - T:type 的缩写
  21. <a name="RPmcQ"></a>
  22. ### 函数定义中的泛型
  23. - 泛型函数:
  24. - 参数类型
  25. - 返回类型
  26. 替换成字符类型的切片
  27. ```rust
  28. fn main() {
  29. let number_list = vec![34, 50, 25, 100, 65];
  30. let result = largest(&number_list);
  31. println!("The largest number is {}", result);
  32. let char_list = vec!['y', 'm', 'a', 'q'];
  33. let result = largest(&char_list);
  34. println!("The largest char is {}", result);
  35. }
  36. fn largest(list: &[i32]) -> i32 {
  37. let mut largest = list[0];
  38. for &item in list {
  39. if item > largest {
  40. largest = item;
  41. }
  42. }
  43. largest
  44. }

把 largest 使用泛型来进行定义 fn largest<T>(list: &[T]) -> T {}

函数 largest 它有泛型的类型参数 T,它接受一个名为 list 的参数,类型为切片,切片里的元素类型是 T。 而这个函数的返回值类型也是 T。

  1. fn main() {
  2. let number_list = vec![34, 50, 25, 100, 65];
  3. let result = largest(&number_list);
  4. println!("The largest number is {}", result);
  5. let char_list = vec!['y', 'm', 'a', 'q'];
  6. let result = largest(&char_list);
  7. println!("The largest char is {}", result);
  8. }
  9. fn largest<T>(list: &[T]) -> T {
  10. let mut largest = list[0];
  11. for &item in list {
  12. if item > largest {
  13. largest = item;
  14. }
  15. }
  16. largest
  17. }

运行出错

  1. error[E0369]: binary operation `>` cannot be applied to type `T`
  2. --> src/main.rs:14:17
  3. |
  4. 14 | if item > largest {
  5. | ---- ^ ------- T
  6. | |
  7. | T
  8. |
  9. help: consider restricting type parameter `T`
  10. |
  11. 11 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
  12. | ^^^^^^^^^^^^^^^^^^^^^^
  13. error: aborting due to previous error
  14. For more information about this error, try `rustc --explain E0369`.
  15. error: could not compile `fanxing`
  16. To learn more, run the command again with --verbose.

比较大小的运算符,不能应用于类型 T,consider restricting type parameter T 考虑限制这个类型参数 T。 让它实现 > std::cmp::PartialOrd 这个 trait,用来实现比较功能(相当于其他语言的接口)。 也就是说,不是所有类型都可以比较大小,只有实现了这个 trait 的类型才可以比较大小。 后面会学习在泛型类型参数中指定特定的 trait。

struct 定义中的泛型

  1. struct Point<T> {
  2. x: T,
  3. y: T,
  4. }
  5. fn main() {
  6. let integer = Point { x: 5, y: 10 };
  7. let float = Point { x: 1.0, y: 4.0 };
  8. }

如何让 x 和 y 是不同类型的呢?

  1. struct Point<T, U> {
  2. x: T,
  3. y: U,
  4. }
  5. fn main() {
  6. let integer = Point { x: 5, y: 10 };
  7. let float = Point { x: 1, y: 4.0 };
  8. }

这样既可以是不同的类型,也可以是相同的类型。

  • 可以使用多个泛型的类型参数

    • 太多类型参数:表示代码需要重组为多个更小的单元

      enum 定义中的泛型

  • 可以让枚举的变体持有泛型数据类型

    • 例如 Option<T>, Result<T, E>
      1. enum Option<T> {
      2. Some(T), // 持有 T 类型值
      3. None, // 不持有值
      4. }
      5. enum Result<T, E> {
      6. Ok(T),
      7. Err(E),
      8. }
      9. fn main() {}

      方法定义中的泛型

  • 为 struct 或 enum 实现方法的时候,可以在定义中使用泛型。

    1. struct Point<T> {
    2. x: T,
    3. y: T,
    4. }
    5. impl<T> Point<T> {
    6. fn x(&self) -> &T {
    7. &self.x
    8. }
    9. }
    10. // 针对具体的类型实现方法
    11. impl Point<i32> {
    12. fn x1(&self) -> &i32 {
    13. &self.x
    14. }
    15. }
    16. fn main() {
    17. let p = Point { x: 5, y: 10 };
    18. println!("p.x = {}", p.x);
    19. }
  • 注意:

    • 把 T 放在 impl 关键字后,表示在类型 T 上实现方法
      • 例如:impl<T> Point<T>
    • 只针对具体类型实现方法(其余类型没实现方法)
      • 例如:impl Point<i32>
    • struct 里的泛型类型参数可以和方法的泛型类型参数不同

      1. struct Point<T, U> {
      2. x: T,
      3. y: U,
      4. }
      5. impl<T, U> Point<T, U> {
      6. fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
      7. Point {
      8. x: self.x,
      9. y: other.y,
      10. }
      11. }
      12. }
      13. fn main() {
      14. let p1 = Point { x: 5, y: 4 };
      15. let p2 = Point { x: "Hello", y: 'c' };
      16. let p3 = p1.mixup(p2);
      17. println!("p3.x = {}, p3.y = {}", p3.x, p3.y); // p3.x = 5, p3.y = c
      18. }

      泛型代码的性能

  • 使用泛型的代码和使用具体类型的代码运行速度是一样的。

  • 单态化 (monomorphization):
    • 在编译时将泛型替换为具体类型的过程
      1. fn main() {
      2. let integer = Some(5); // std::option::Option<i32>
      3. let float = Some(5.0); // std::option::Option<f64>
      4. }
      编译时转换为具体类型,如下 ```rust enum Option_i32 { Some(i32), None, }

enum Option_f64 { Some(f64), None, }

fn main() { let integer = Option_i32::Some(5); let float = Option_f64::Some(5.0); } ```