泛型

  • 提高代码复用能力
  • 泛型是具体类型或其他属性的抽象代替
  • 编写的代码不是最终的代码、而是一种模板,里面有一些“占位符”
  • 编译器在编译时将“占位符”替换为具体的类型。

函数泛型

例如:fn largest<T>(list:&[T]) -> T{ ... }

结构体泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct User<T>{
x: T,
y: T,
}

struct Users<T, U>{
x: T,
y: U,
}

fn main() {
let integer = User {x: 5, y: 10};
let double = User {x: 1.0, y: 2.0};
let mix = User {x: 5, y: 10.0};

}

枚举泛型

1
2
3
4
5
6
7
8
9
enum Option<T> {
Some(T),
None,
}

struct Result<T, E>{
x: T,
y: E,
}

方法泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Point<T> {
x: T,
y: T,
}

impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
} //针对所有的类型实现方法

impl Point<i32> {
fn x1(&self) -> &i32 {
&self.x
}
} //针对具体的 i32 实现方法

泛型的运行速度和普通代码运行速度一致,这是由于 Rust 在编译时将这些泛型单态化。即将他们的类型根据代码确定下来。

Trait

引入

  • Trait 告诉 Rust 编译器,某种类型具有哪些并且可以与其他类型共享的功能。
  • Trait:抽象的定义共享行为。
  • Trait bounds(约束):泛型类型参数指定为实现了特定行为的类型。
  • 类似于其他语言的接口

定义

把方法签名放在一起,来定义实现某种目的所必须的一组行为。

  • 关键字:trait
  • 只有方法签名,没有具体实现
  • trait 可以有多个方法:每个方法签名占一行,以;结尾
  • 实现该 trait 的类型必须提供具体的方法实现

在类型上实现 trait

  • 关键字:`impl tarit_name for struct_name { … }
  • 在 impl 的块里面,需要对 Trait 里的方法签名进行具体的实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 NewsArticle {
fn summarize(&self) -> String {
format!("{}:{}", self.username, self.content);
}
}

可以在某个类型上实现某个 trait 的前提条件是:这个类型或这个 trait 是在本地 crate 里定义的。
无法为外部类型来实现外部的 trait

默认实现

  • 默认实现的方法可以调用 trait 中的其他方法,即使这些方法没有默认实现。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    pub trait Summary {
    fn summarize(&self) -> String {
    format!("(Read more...)");
    }
    }

    impl Summary for NewsArticle {
    //空,采用默认实现
    }

默认实现是区别于接口的重要区分,我们可以在 Trait 的定义中实现默认实现,结构体可以是用这个默认实现。

Trait 作为参数

  • 使用impl trait
1
2
3
pub fn notify(item: impl Summary) { //这样声明,只要类型实现了该trait 就可以传入。
println!("Breaking news {}", item.summarize());
}
  • 使用trait bound
1
2
3
pub fn notify<T: Summary>(item: T) { //这样声明,只要类型实现了该trait 就可以传入。
println!("Breaking news {}", item.summarize());
}
  • 实现多个trait,使用+
1
2
3
4
5
6
7
pub fn notify<T: Summary + Display>(item: T) { 
println!("Breaking news {}", item.summarize());
}

pub fn notify(item: impl Summary + Display) {
println!("Breaking news {}", item.summarize());
}
  • 简化 trait 约束,使用 where,避免函数签名紊乱
1
2
3
4
5
6
7
8
9
10
11
pub fn notify<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
println!("Breaking news {}", item.summarize());
}

pub fn notify<T, U>(a: T, b: U) -> String
where
T: Summary + Display,
U: Clone + Debug,
{
println!("Breaking news {}", item.summarize());
}

Trait 作为返回类型

  • impl Trait 只能返回确定的同一种类型,返回可能不同类型的代码会报错。
1
2
3
4
5
pub fn notify1(s: &str) -> impl Summary {
NewsArticle {
....
}
}

其他

  • 在使用泛型类型参数的 impl 块上使用 Trait bound,我们可以有条件的为实现了特定 Trait 的类型来实现方法
1
2
3
4
5
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
..
}
} //只有实现了Display + PartialOrd的泛型才有这个方法
  • 可以为实现了其他 Tarit 的任意类型有条件的实现某个 Trait.
1
2
3
impl<T: fmt::Display> ToString for T {
....
} //为任何实现了 Dispaly Tarit 的类型都实现 ToString.