Skip to content
Published at:

泛型数据类型Generic Data Types

泛型是具体类型或其他属性的抽象替代,高效的处理重复概念的工具

函数Function中

rust
fn largest<T>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let list = vec![1, 2, 3, 4, 5, 6, 7];
    let num = largest(&list);
    println!("num = {:?}", num);
}

结构体中

rust
struct Pointer<T, U> {
    x: T,
    y: U,
}

fn main() {
    let integer = Pointer { x: 1, y: 2 };
    let float = Pointer { x: 1.0, y: 2.0 };
  	let mul = Pointer { x: 10, y: 20};
}

enum中

rust
enum Option<T> {
    Ok(T),
    None(),
}
enum Result<T, E> {
    Ok(T),
    Err(E),
}

在方法Method中

rust
#[derive(Debug)]
struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn x(&self) -> &T {
        &self.x
    }
}

impl Point<f32, f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

impl<T, U> Point<T, U> {
    fn mixup<K, W>(self, other: Point<K, W>) -> Point<T, W> {
        Point {
            x: (self.x),
            y: (other.y),
        }
    }
}

fn main() {
    let integer = Point { x: 1, y: 2 };
    let float: Point<f32, f32> = Point { x: 1.0, y: 2.0 };
    let x = integer.x;
    println!("x = {:?}", x);

    let s = float.distance_from_origin();
    println!("s = {:?}", s);

    let mix = Point { x: 10, y: 20.0 };
    let mix = float.mixup(mix);
    println!("mix = {:#?}", mix);
}

泛型代码的性能

Rust 实现了泛型,使得使用泛型类型参数的代码相比使用具体类型并没有任何速度上的损失

Rust 通过在编译时进行泛型代码的 **单态化(monomorphization)**来保证效率。单态化是一个 通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程,

编译器寻找所有泛型代 码被调用的位置并使用泛型代码针对具体类型生成代码

rust
enum Option_i32 {
    Some(i32),
    None,
}

enum Option_f64 {
    Some(f64),
    None,
}

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

trait:定义共享的行为

trait:这是一个定义泛型行为的方法。trait 可以与泛型结合来将泛型限制为 拥有特定行为的类型,而不是任意类型

某个特定类型拥有可能与其他类型共享的功能

类似于其它语言中的接口

  • 一个trait可以有多个行为
  • 语法:一行一个方法签名且都以分号结尾
rust
pub trait Summary {
	fn summarize(&self) -> String;
}

为类型实现 trait

rust
pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}
impl Summary for NewsArticle {
    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,
}
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

默认实现

trait 作为参数

rust
pub fn notify(item: impl Summary) -> String {
    format!("breaking news! {}", item.summarize())
}

通过 + 指定多个 trait bound

rust
pub fn notify(item: impl Summary + Display) {}
pub fn notify<T: Summary + Display>(item: T) {}

通过 where 简化 trait bound

rust
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {}
// 简化后
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug,
{}

返回实现了 trait 的类型

rust
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false,
    }
}

使用 trait bound 有条件地实现方法

生命周期与引用有效性

Rust 中的每一个引用都有其生命周期(lifetime),也就是引用保持有效的作用域

当因为有多种可能类型的时候必须注明类型,也会出现引用的生命周期以一些不同方式相关联的情况,所以 Rust 需要我们使用泛型生命周期参数来注明他们的关系,这样就能确保运行时实际使用的引用绝对是有效的

它是一类允许我们向编译器提供引用如何相互关联的泛 型。Rust 的生命周期功能允许在很多场景下借用值的同时仍然使编译器能够检查这些引用的 有效性。

生命周期避免了悬垂引用

rust
fn main() {
    let x;
    {
        let y = 20;
        x = &y;
    }
    println!("x = {:?}", x);
}

借用检查器

Rust 编译器有一个 借用检查器(borrow checker),它比较作用域来确保所有的借用都是有效的。

rust会去检查一个变量的生命周期范围

函数中的泛型生命周期

生 命周期注解描述了多个引用生命周期相互的关系

格式:

rust
&i32 // 引用
&'a i32 // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用

深入理解生命周期

生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或 是违反内存安全的行为

结构体定义中的生命周期注解

生命周期省略(Lifetime Elision)

省略规则并不提供完整的推断:如果 Rust 在明确遵守这些规则的前提下变量的生命周期仍然 是模棱两可的话,它不会猜测剩余引用的生命周期应该是什么。在这种情况,编译器会给出 一个错误,这可以通过增加对应引用之间相联系的生命周期注解来解决。

判断引用何时不需要明确的注解:

  • 第一条规则是每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数: fn foo<'a>(x: &'a i32) ,有两个引用参数的函数有两个不同的生命周期参数, fn foo<'a, 'b>(x: &'a i32, y: &'b i32) ,依此类推。
  • 第二条规则是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数: fn foo<'a>(x: &'a i32) -> &'a i32 。
  • 第三条规则是如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为 &self 或 &mut self ,那么 self 的生命周期被赋给所有输出生命周期参数。第三条规则使得方法更容易读写,因为只需更少的符号。

方法定义中的生命周期注解

静态生命周期

'static ,其生命周期能够存活于整个程序期间。所有的字符串字面值都拥有 'static 生命周期

rust
 let s: &'static str = "I have a static lifetime.";

结合泛型类型参数、trait bounds 和生命周期

语法

rust
use std::fmt::Display;
fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
where
    T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}