高级特征

关联类型

之前的例子中出现过Item这个占位符

  1. 关联类型在trait中指定占位符类型
    (1) 关联类型是一个将类型占位符与trait相关联的方式
    (2) trait的实现者会针对特定的实现在这个类型的位置指定相应的具体类型,可以通过这样使用多种类型的trait
1
2
3
4
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
  • 泛型和关联类型的区别
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
30
31
32
33
34
35
36
pub trait Iterator1<T> {
fn next(&mut self) -> Option<T>;
}

struct A {
value: i32,
}
impl Iterator1<i32> for A {
fn next(&mut self) -> Option<i32> {
println!("in i32");
if self.value > 3 {
self.value += 1;
Some(self.value)
} else {
None
}
}
}
impl Iterator1<String> for A {
fn next(&mut self) -> Option<String> {
println!("in string");
if self.value > 3 {
self.value += 1;
Some("hello".to_string())
} else {
None
}
}
}
fn main() {
let a = 4;
//a.next(); //报错,编译器推导不出具体的类型,因为实现了两个不同的类型
//使用完全限定语法可以执行
<A as Iterator1<i32>>::next(&mut a);//类似强制转换
<A as Iterator1<String>>::next(&mut a);
}

这种写法比较烦锁,使用自定义迭代器中的关联类型较为好些:
😘自定义迭代器

默认泛型类型参数和运算符重载

  1. 使用泛型类型参数时,可以为泛型指定一个默认的具体操作
  2. 运算符重载指在特定情况下自定义运算符行为的操作
  3. 可以进行重载的运算符或者自定义运算符只能是Rust已经有的运算符,如我们可以为std::ops
    中的运算符和相应的trait实现运算符相关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
30
31
32
33
34
35
36
37
38
use std::ops::Add;
#[derive(Debug,PartialEq)]
struct Point {
x: i32,
y: i32,
}

impl Add for Point {
type Output = Point;
fn add(self,other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
struct Millimeters(u32);

struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self,other: Meters) -> Millimeters {
Millimeters(self.0+other.0*1000)
}
}

fn main() {
assert_eq!(Point{x:1,y:1}+ Point {x:2,y:3},Point{x:3,y:4});
let mi = Millimeters(1);
let m = Meters(1);
let r = mi + m;
println!("r = {:?}",r);
}

//trait Add<RHS = Self> { //尖括号里面是默认类型参数,RHS是一个泛型类型参数
// type Output;
// fn add(self,rhs: RHS) -> Self::Output;
// }

完全限定语法

  • 同名方法(有self)
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
30
31
32
33
trait A {
fn print(&self);
}
trait B {
fn print(&self);
}

struct MyType;

impl A for MyType {
fn print(&self) {
println!("A trait for MyType");
}
}
impl B for MyType {
fn print(&self) {
println!("B trait for MyType");
}
}
impl MyType {
fn print(&self) {
println!("MyType");
}
}

fn main() {
let m = MyType;
m.print();//调用自己本身的方法

A::print(&m);//调用a的print
B::print(&m);
MyType::print(&m);//调用自己的
}
  • 关联函数

::function(…)

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
trait Animal {
fn baby_name() -> String;

}

struct Dog;

impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}

impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}

fn main() {
println!("baby_name: {}",Dog::baby_name()); //调用自己的关联函数
//错误方法
//println!("baby_name: {}",Animal::baby_name()); //调用自己的关联函数
println!("baby_name: {}",<Dog as Animal>::baby_name()); //强制转换的完全限定语法

}

父Trait

某些时刻需要某个trait使用另外一些trait的功能,需要能够依赖的trait也被实现,这种
trait就是父trait

  • 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::fmt;
trait OutPrint: fmt::Display {//OutPrint这个trait要求实现Display这个trait
fn out_print(&self) {
let output = self.to_string();
println!("output {}",output);
}
}

struct Point {
x: i32,
y: i32,
}

impl OutPrint for Point {}
//需要使用fmt::Display为Point
impl fmt::Display for Point {
fn fmt(&self,f :&mut fmt::Formatter) -> fmt::Result {
write!(f,"({},{})",self.x,self.y);
}
}
fn main() {

}

newtype模式

newtype模式用来在外部类型上实现外部Trait

  1. 孤儿规则(orphan rule):只要trait或者类型对于当前crate是本地的话就可以在此类型上
    实现该Trait
  2. 不是本地地一个绕开这个限制的方法是使用newtype模式(newtype pattern)
1
2
3
4
5
6
7
8
9
10
11
12
13
use std::fmt;
struct Wrapper(Vec<String>);//将Vec作为成员绕开孤儿规则
//由于Vec和Display都不是本地的,所以会造成孤儿规则
impl fmt::Display for Wrapper {
fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{},{}",self.0.join(","));
}
}

fn main() {
let w = Wrapper(vec![String::from("hello"),String::from("world")]);
println!("w = {}",w); //hello,world
}