Rust 中的泛型

  |   0 评论   |   0 浏览

数据类型是对同一类数据的抽象,而泛型是对具有一组相同行为的数据类型的抽象。Rust 使用 trait 来描述这一组相同的行为,简单的说 trait 就是一组函数的集合。Rust中的trait类似于其他语言中的常被称为接口(interfaces)的功能。

泛型

在函数中使用泛型

假如有两个函数,它们的功能是查找slice中最大值并返回,函数体中的采用的算法是一样的,主要不同的是参数类型。如下所示:

 1fn largest_i32(list: &[i32]) -> i32 {
 2    let mut largest = list[0];
 3
 4    for &item in list.iter() {
 5        if item > largest {
 6            largest = item;
 7        }
 8    }
 9
10    largest
11}
12
13fn largest_char(list: &[char]) -> char {
14    let mut largest = list[0];
15
16    for &item in list.iter() {
17        if item > largest {
18            largest = item;
19        }
20    }
21
22    largest
23}

遇到这种情况我们就可以将重复部分抽取出来,用泛型代替参数中的数据类型。

 1// T 是泛型类型,它代表所有的数据类型。
 2fn largest<T>(list: &[T]) -> T {
 3    let mut largest = list[0];
 4
 5    for &item in list.iter() {
 6        if item > largest {
 7            largest = item;
 8        }
 9    }
10
11    largest
12}
13// 这样“任何”类型的`slice`都可以使用该函数了,而不用单独为每个类型都定义一个函数
14fn main() {
15    let number_list = vec![34, 50, 25, 100, 65];
16
17    let result = largest(&number_list);
18    println!("The largest number is {}", result);
19
20    let char_list = vec!['y', 'm', 'a', 'q'];
21
22    let result = largest(&char_list);
23    println!("The largest char is {}", result);
24}

但是,上面的示例是不能编译通过的,因为存在两个问题:

  • 函数体中的大于运算符(>)用于比较两个 T 类型的值,但并不是所有的数据类型都能使用这个运算符。
  • list参数的类型有可能是没有实现 Copy trait 的,这意味着我们可能不能将 list[0] 的值移动到 largest 变量中。

如果要完善这个函数,需要为T类型指定相应的trait,来进行限定。

结构体中使用泛型

可以使用 <> 语法来定义拥有一个或多个泛型参数类型字段的结构体。

示例一:指定一个泛型参数

1struct Point<T> {
2    x: T,
3    y: T,
4}
5// x 和 y 的类型必须相同
6fn main() {
7    let integer = Point { x: 5, y: 10 };
8    let float = Point { x: 1.0, y: 4.0 };
9}

示例二:指定两个泛型参数

 1struct Point<T, U> {
 2    x: T,
 3    y: U,
 4}
 5// x 和 y 的类型可以不同
 6fn main() {
 7    let both_integer = Point { x: 5, y: 10 };
 8    let both_float = Point { x: 1.0, y: 4.0 };
 9    let integer_and_float = Point { x: 5, y: 4.0 };
10}

枚举中使用泛型

与结构体类似,枚举中也可以使用泛型。

示例一:标准库中的Option<T>

1enum Option<T> {
2    Some(T),
3    None,
4}

示例二:标准库中的Result<T, E>

1enum Result<T, E> {
2    Ok(T),
3    Err(E),
4}

方法定义中使用泛型

示例:在 Point<T> 结构体上实现方法 x,它返回 T 类型的字段 x 的引用

 1struct Point<T> {
 2    x: T,
 3    y: T,
 4}
 5
 6impl<T> Point<T> {
 7    fn x(&self) -> &T {
 8        &self.x
 9    }
10}

除了使用泛型,也可以为特定的数据类型定义一个方法。

示例

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

trait

使用trait可以帮助我们确保类型拥有期望的行为,对数据类型进行限定。

定义 trait

使用 trait关键字定义 trait

示例

1pub trait Summary {
2    // 可以有多个方法:一行一个方法签名且都以分号结尾。
3    fn summarize(&self) -> String;
4}

实现 trait

示例

 1pub struct NewsArticle {
 2    pub headline: String,
 3    pub location: String,
 4    pub author: String,
 5    pub content: String,
 6}
 7
 8impl Summary for NewsArticle {
 9    fn summarize(&self) -> String {
10        format!("{}, by {} ({})", self.headline, self.author, self.location)
11    }
12}
13
14pub struct Tweet {
15    pub username: String,
16    pub content: String,
17    pub reply: bool,
18    pub retweet: bool,
19}
20
21impl Summary for Tweet {
22    fn summarize(&self) -> String {
23        format!("{}: {}", self.username, self.content)
24    }
25}

默认实现

trait中可以提供默认的实现,当某个特定的类型实现trait时,可以选择保留或者重载这个默认的实现。

示例

 1// Summary trait 的定义,带有一个 summarize 方法的默认实现
 2pub trait Summary {
 3    fn summarize(&self) -> String {
 4        String::from("(Read more...)")
 5    }
 6}
 7// NewsArticle 类型使用这个默认实现
 8impl Summary for NewsArticle {}
 9// 重载这个默认实现
10impl Summary for NewsArticle {
11    fn summarize(&self) -> String {
12        format!("{}, by {} ({})", self.headline, self.author, self.location)
13    }
14}

trait 作为参数

trait代表的是一组函数,因此可以用它来指定那些实现了特定trait的数据类型。

示例一

1// 任何实现了 Summary 的类型都可以使用这个函数
2// 可以认为是在泛型的基础上,添加了 trait 限定。这种语法称之为 trait bound。
3pub fn notify<T: Summary>(item: T) {
4    println!("Breaking news! {}", item.summarize());
5}
6// 这是对上面语法形式的简写
7pub fn notify(item: impl Summary) {
8    println!("Breaking news! {}", item.summarize());
9}

示例二:通过 + 指定多个 trait,数据类型必须实现指定的多个 trait

1// 使用 impl 关键字
2pub fn notify(item: impl Summary + Display) {}
3// 使用 trait bound 语法
4pub fn notify<T: Summary + Display>(item: T) {}

示例三:通过 where 简化 trait bound

1fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {}
2// 上面的函数可以简化成下面这种形式
3fn some_function<T, U>(t: T, u: U) -> i32
4    where T: Display + Clone,
5              U: Clone + Debug
6{}

示例四:返回实现了 trait 的数据类型

1fn returns_summarizable() -> impl Summary {
2    Tweet {
3        username: String::from("horse_ebooks"),
4        content: String::from("of course, as you probably already know, people"),
5        reply: false,
6        retweet: false,
7    }
8}

相关资料

The Rust Programming Language

Rust by Example