1. 在rust中运用相关类型来制定占位类型
相关类型(assciated type)是trait中类型占位符,它能够用于trait的办法签名中。详细地说能够定义出包含某些类型的trait,而在完成前无需知道这些类型是什么。
如下示例代码:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
fn main() {
println!("Hello, world");
}
2. 相关类型与泛型的差异
每次完成泛型trait的时分必须标示详细的类型,而且能够为一个类型屡次完成某个Trait(运用不同的泛型参数);而完成相关类型trait的时分,咱们无需标示类型,但是要在里边指明详细的相关类型,而且咱们无法为单个类型屡次完成某个Trait。
如下示例代码:
// 相关类型Trait
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
// 泛型类型Trait
pub trait Iterator2<T> {
fn next(&mut self) -> Option<T>;
}
// struct
struct Counter {}
// 完成相关类型的Trait
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
// 完成泛型类型的trait
impl Iterator2<String> for Counter {
fn next(&mut self) -> Option<String> {
None
}
}
// 再次完成泛型类型的trait
impl Iterator2<u32> for Counter {
fn next(&mut self) -> Option<u32> {
None
}
}
fn main() {
println!("Hello, world!");
}
3. 默许泛型参数和运算符重载
能够在运用泛型参数时为泛型指定一个默许的详细类型,语法为:<PlaceholderType=ConcreteType>
,这种技术常用于运算符重载(operator overloading)。尽管Rust不允许创立自己的运算符以及重载任意的运算符,但能够经过完成std::ops
中列出的那些trait来重载一部分相应的运算符。
如下示例代码:
use std::ops::Add;
#[derive(Debug, PartiaEq)]
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,
}
}
}
fn main() {
assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 });
}
在上面的示例代码中,std::ops::Add
trait 运用默许的泛型参数类型为 <Rhs = Self>
,也便是说,在咱们完成std::ops::Add
trait的过程中,如果没有为Rhs
指定一个详细的参数类型,那么Rhs
类型就默许为Self
,所以上面的代码中Rhs
便是Self
即Point
。
下面咱们做一个毫米和米相加的比如,就需求指定泛型参数类型。
如下示例代码:
use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add(Meters) for Millimeters {
type Output = Millimeters;
fn add(self, other: Point) -> Point {
Millimeters(self.0 + (other.o * 1000))
}
}
fn main() {
}
4. 默许泛型参数的主要运用场景
主要有两个运用场景,如下
- 扩展一个类型而不损坏现有的代码
- 允许在大部分用户都不需求的特定场景下进行自定义
5. 彻底限制语法(Fully Qualified Syntax)与怎么调用同名办法
如下示例代码:
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your caption speaking.")
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
fn mian() {
let person = Human;
person.fly();
}
在上面示例代码中,两个struct别离完成了Human
trait,完成了fly
办法,而且Human
trait有自己的fly办法。最终在main
函数中调用了fly
办法,那么会调用到哪个办法呢?在上面的示例代码中,将调用自身的fly
办法。此刻又有一个问题,那么怎么调用完成了两个trait的办法。
如下示例代码:
fn mian() {
let person = Human;
// 调用自身的办法
person.fly();
// 调用Pilot的办法
Pilot::fly(&person);
// 调用Wizard的办法
Wizard::fly(&person);
}
需求留意的是,Pilot
能够被多个struct完成,那么怎么知道为什么调的是Human
的办法,是因为有self
参数,传入的&person
参数中它便是Human
类型。咱们再来看一个不一样的示例
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!("A baby dog is called a {}", Dog::baby_name());
}
在上面的示例代码中,Dog
自身有baby_name
相关办法,它输出的是“Spot”,那么怎么调用Animal
的办法呢?这时分咱们能够运用彻底限制语法。
彻底限制语法的格式为:<Type as Trait>::function(receiver_if_method, next_arg, ...)
,这种语法能够在任何调用函数或办法的地方运用,而且它允许疏忽那些从上下文能推导出来的部分。但要记住的是,当rust无法区别咱们调用哪个详细完成的时分,才需运用这种语法,因为这种语法写起来比较麻烦,不应轻易运用。如下示例代码
// ...
fn main() {
// 调用自身的办法
println!("A baby dog is called a {}", Dog::baby_name());
// 调用完成Animal的办法
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
6. 运用supertrait来要求trait附带其他trait的功用
其实相当于一个trait承继另外一个trait。有时分咱们需求再一个trait中运用其他trait的功用,也便是说需求直接被依靠的trait也被完成,那个被直接依靠的trait便是当前trait的supertrait。假如咱们希望一个trait有打印功用的办法,并希望打印的办法能够调用to_string
办法,完成如下示例代码:
use std::fmt;
// 依靠于fmt::Display
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", "*".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
struct Point {
x: i32,
y: i32,
}
// 为Point完成OutlinePrint
impl OutlinePrint for Point {}
// Point完成了OutlinePrint就必须完成fmt::Display
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {}
7. 运用newtype形式在外部类型上完成外部trait
孤儿规矩:只有当trait或类型定义在本地包时,才能为该类型完成这个trait。咱们能够运用newtype形式来绕过这规矩,即运用tuple struct(元组结构体)创立一个新的类型放在本地。如咱们想为Vec
完成fmt::Display
trait,而Vec
和fmt::Display
都在定义在外部包中,所以咱们无法直接为Vec
完成fmt::Display
。
完成方案如下:
use std::fmt;
// 定义Wrapper元组结构体,包住Vec
struct Wrapper(Vec<String>);
// 为Wrapper完成Display
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 运用selef.0即取出vec
write!("[{}]", selef.0.join(", "));
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}