九、Rust 高级特性

213 阅读5分钟

九、Rust 高级特性

1. 泛型

  • Rust 中的泛型,和其他语言中的泛型概念是相同的,但也有一些 Rust 特有的特性

  • 可以使用泛型的地方

    • 函数
    • 结构体
    • 枚举
    • 结构体/枚举的方法
  • Rust 结构体特有的特性

    • 可推断性:结构体/枚举的泛型是可推断的

      struct Point<T> {
          x: T,
          y: T,
      }
      
      let p1 = Point { x: 4, y: 5 }; // 可行
      let p2 = Point { x: 4, y: 5.0 }; // 不可行,类型不兼容,与泛型不匹配
      
    • 编译时单态化

      • 单态化:编译时,Rust 使用具体类型,来将泛型代码转换为特定类型代码的过程
      • Rust 通过在编译时进行泛型代码的 单态化monomorphization)来保证效率
  • Rust 中特有的泛型:生命周期泛型

    • 这个概念将在 3. 生命周期 中介绍

2. trait

  • 作用:告诉 Rust 编译器,某个特定类型拥有可能与其他类型共享的功能

  • trait 是将方法签名组合起来的一种方式

    • trait 中可以包含一些方法的默认实现
    • 目的: 定义一个实现某些目的所必需的行为的集合
    • 个人理解:trait 的作用类似于 Java/C++/Typescript 等语言中的 abstract class
  • 作用域限制

  • 只有当 trait 或者要实现 trait 的类型位于 crate 的本地作用域时,才能为该类型实现 trait

  • 基本用法

    // 来自官方教程的一个示例
    // 这是一个 public 的 trait,可以在其他模块中引入
    pub trait Summary {
        // 这个方法只定义了签名,没有定义实现。实现这个 trait 的结构体应当实现这个方法
        fn summarize_author(&self) -> String;
        // 这个方法有一个默认的实现,实现这个 trait 的结构体可以实现自己的同名方法,来覆盖这个默认的实现
        fn summarize(&self) -> String {
            format!("(Read more from {}...)", self.summarize_author())
        }
    }
    
    pub struct NewsArticle {
        pub headline: String,
        pub location: String,
        pub author: String,
        pub content: String,
    }
    
    // 为 NewsArticle 结构体实现 Summary trait
    impl Summary for NewsArticle {
        // 这个方法没有默认实现,所以这里必须实现
        fn summarize_author(&self) -> String {
            format!("@{}", self.author)
        }
        // 这个实现会覆盖默认实现
        fn summarize(&self) -> String {
            format!("{}, by {} ({})", self.headline, self.author, self.location)
        }
    }
    
    pub struct Tweet {
        pub username: String,
        pub content: String,
        pub reply: bool,
        pub retweet: bool,
    }
    
    // 为 Tweet 结构体实现 Summary trait
    impl Summary for Tweet {
        // 因为 summarize 有一个默认实现,所以这里不是必须实现,可以省略
        // summarize_author 没有默认实现,所以这里必须实现
        fn summarize_author(&self) -> String {
            format!("@{}", self.username)
        }
    }
    
    fn notify(item: impl Summary) {
        println!("Breaking news! {}", item.summarize());
        // 下一行代码会报错,因为 item 没有实现 std::fmt::Display
        // println!("Breaking news! {}", item);
    }
    
    
  • 高级用法

    // 续上一个示例
    
    // trait bound 语法
    fn notify2<T: Summary>(item: T) {}
    fn notify3<T: Summary>(i1: T, i2: T) {}
    
    // 指定多个 trait bound
    use std::fmt::Display;
    fn notify4<T: Summary + Display>(item: T) {
      println!("Breaking news! {}", item);
    }
    
    // 来自官方教程的一个示例:使用 where 从句
    use std::fmt::Debug;
    fn some_function<T, U>(t: T, u: U) -> i32
    where
      T: Display + Clone,
      U: Clone + Debug,
    {
      return 1;
    }
    
    impl Tweet {
      /// 返回一个实现了指定 trait 的类型
      ///
      /// * 支持: impl 结构体/泛型, 函数
      /// * 不支持: impl trait
      ///
      /// 只有这一种写法,没有 trait bound 的写法
      fn some_fn() -> impl Summary {
        return Tweet {
          username: String::from("horse_ebooks"),
          content: String::from("of course, as you probably already know, people"),
          reply: false,
          retweet: false,
        };
      }
    }
    
    // 官方教程的一个示例
    pub fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
      let mut largest = list[0];
    
      for &item in list.iter() {
        if item > largest {
          largest = item;
        }
      }
    
      return largest;
    }
    
    

3. 生命周期

  • 针对的目标:租借值(引用)

  • 生命周期:引用保持有效的作用域

    • “生命周期的概念某种程度上说不同于其他语言中类似的工具,毫无疑问这是 Rust 最与众不同的功能”
  • 借用检查器 (borrow checker) :通过比较作用域来确保所有的借用都是有效的

  • 生命周期语法注解,即生命周期泛型

    • 注意:生命周期注解出现在签名中,但是不存在于函数体中的任何代码中
  • 生命周期注解存在的地方

    • 函数
    • 包含引用的结构体
  • 示例代码

    // 函数中的生命周期注解
    
    ///
    /// * 纯引用: &str
    /// * 可变纯引用: &mut str
    /// * 指定生命周期的引用: &'a str
    /// * 指定生命周期的可变引用: &'a mut str
    ///
    /// 所以说,尖括号内的不一定是类型泛型,也可能是生命周期注解
    /// 注解 'a 中的 a 是任意合法命名即可
    fn longer_str<'a>(s1: &'a str, s2: &'a str) -> &'a str {
        return match s1.len() > s2.len() {
            true => s1,
            false => s2,
        };
    }
    
    // 包含引用的结构体中的生命周期注解
    
    // &str 是一个引用,在结构体中定义这个类型时必须使用生命周期注解,其他类型的引用也必须这样
    struct Person<'a> {
        name: &'a str,
        gender: u8,
        age: u8,
    }
    
    // 为带有生命周期注解的结构体实现方法时,也必须把生命周期注解带上
    impl<'a> Person<'a> {
        fn get_gender(&self) -> &str {
            // 注意这里的返回值哦,是用的比较特殊的简写方式
            match &self.gender {
                0 => "girl",
                1 => "boy",
                _ => "",
            }
        }
    
        pub fn introduce(&self) {
            let gender_str = self.get_gender();
            println!(
                "{} is a {}, {} years old this year.",
                self.name, gender_str, self.age
            );
        }
    }
    
    // 函数中的生命周期注解
    fn get_man<'a>(name: &'a str) -> Person {
        Person {
            name,
            age: 0,
            gender: 1,
        }
    }
    
    // 函数中的生命周期注解
    fn get_woman<'a>(name: &'a str) -> Person {
        Person {
            name,
            age: 0,
            gender: 0,
        }
    }
    
    
  • 生命周期的分类

    • 输入生命周期:函数或方法的 参数 的生命周期
    • 输出生命周期:函数或方法的 返回值 的生命周期
  • 生命周期省略规则

    • 每一个引用类型参数都有它自己的生命周期参数
      • 多个引用类型参数的生命周期可以相同,也可以不同
    • 如果只有一个输入生命周期参数,那么输出生命周期与此相同
    • 如果方法有多个输入生命周期参数并且其中一个参数是 &self&mut self,则表明这个方法是一个对象的方法(method),那么所有输出生命周期参数被赋予 self 的生命周期
  • 特殊的生命周期:静态生命周期

    • 注解:'static
    • 能够存活于整个程序的运行期间
    • 所有的字符串字面值的生命周期都是 'static