核心概念
特质
Basics of Trait
在Rust中,特质(Traits)是一种定义方法签名的机制。
特质允许你定义一组方法的签名,但可以不提供具体的实现(也可以提供)。这些方法签名可以包括参数和返回类型,但不包括方法的实现代码。
任何类型都可以实现特质,只要它们提供了特质中定义的所有方法。这使得你可以为不同类型提供相同的行为。
特点:
- 内置常量:特质可以内置常量(
const),特质中定义的常量在程序的整个生命周期内是有效的 - 默认实现:特质可以提供默认的方法实现。如果类型没有为特质中的某个方法提供自定义实现,将会使用默认实现。
- 多重实现:类型可以实现多个特质,这允许你将不同的行为组合在一起。
- 特质边界:在泛型代码中,你可以使用特质作为类型约束。这被称为特质边界,它限制了泛型类型必须实现的特质。
Trait Alias: Rust还支持trait alias,这允许你为复杂的trait组合创建简洁的别名,以便在代码中更轻松地引用。
例子:
trait Greeter {fn greet(&self);fn hello() {println!("hello");}}struct Person {name: String,}impl Greeter for Person {fn greet(&self) {println!("greet {}", self.name);}}fn main() {let person = Person {name: "Yz".to_owned(),};person.greet(); // greet YzPerson::hello(); // hello}
Trait Object & Box
Trait Object
- 在运行时动态分配的对象
- “运行时泛型”
- 比泛型要灵活的多
- 可以在集合中混入不同类型对象
- 更容易处理相似的数据
- 有一些小小的性能损耗
Keyword dyn
dyn是Rust中的关键字,用于声明特质对象(trait object)的类型。特质对象是实现特定特质(trait)的类型的实例,但其具体类型在编译时是未知的。因此,为了让编译器知道我们正在处理的是特质对象,我们需要在特质名称前面加上dyn关键字。
dyn关键字的作用是指示编译器处理特质对象。
Three Forms of Data Transfer in Rust
- 不可变引用
&dyn Trait
- 可变引用
&mut dyn Trait
- Move语义所有权转移
- 特质需要用
Box<dyn Trait>实现Move,如果你需要在函数调用之间传递特质的所有权,并且希望避免在栈上分配的大量内存,可以使用Box<dyn Trait>。
- 特质需要用
Three Ways to define trait Object
第一种:
let o = Object{};let o_obj: &dyn Object = &o;第二种:
let o_obj: &dyn Object = &Object{}第三种:
let o_obj: Box<dyn Object> = Box::new(Object{});
第一种和第二种都是创建不可变引用,第三种最常用也是最灵活,一般来说会使用Box和特质来组成集合元素。
例子:
1struct Obj {}23trait Overview {4 fn overview(&self) -> String;5}67impl Overview for Obj {8 fn overview(&self) -> String {9 String::from("Obj")10 }11}1213// 不可变引用14fn call_obj(item: &impl Overview) {15 println!("Overview {}", item.overview());16}1718// Move19fn call_obj_box(item: Box<dyn Overview>) {20 println!("Overview box {}", item.overview());21}2223trait Sale {24 fn amount(&self) -> f64;25}2627struct Common(f64);2829impl Sale for Common {30 fn amount(&self) -> f64 {31 self.032 }33}3435struct TenDiscount(f64);3637impl Sale for TenDiscount {38 fn amount(&self) -> f64 {39 self.0 - 10.040 }41}4243struct TenPercentDiscount(f64);4445impl Sale for TenPercentDiscount {46 fn amount(&self) -> f64 {47 self.0 * 0.948 }49}505152fn calculate(sales: &Vec<Box<dyn Sale>>) -> f64 {53 sales.iter().map(|sale|sale.amount()).sum()54}5556fn main() {57 let a = Obj{};58 call_obj(&a); // Overview Obj59 println!("{}", a.overview()); // Obj60 let b_a = Box::new(Obj{});61 call_obj_box(b_a); // Overview box Obj62 // println!("{}", b_a.overview())6364 let c = Box::new(Common(100.0));65 let t = Box::new(TenDiscount(100.0));66 let t2 = Box::new(TenPercentDiscount(100.0));6768 let sales: Vec<Box<dyn Sale>> = vec![c, t, t2];69 println!("pay {}", calculate(&sales)) // pay 28070}
Trait Object & Generics
Difference Ways of Writing Generics & impl
fn call(item1: &impl Trait, item2: &impl Trait);可以是不同类型
fn call_generic<T: Trait>(item1: &T, item2: &T);可以是相同类型
Multiple Trait Bounds
fn call(item1: &(impl Trait + AnotherTrait));fn call_generic<T: Trait + AnotherTrait>(item1: &T);
例子:
1trait Overview {2 fn overview(&self) -> String {3 String::from("Course")4 }5}67trait Another {8 fn hell(&self) {9 println!("welcome to hell")10 }11}1213struct Course {14 headline: String,15 author: String,16}1718// Course实现两种特质19impl Overview for Course {}20impl Another for Course {}2122struct AnotherCourse {23 headline: String,24 author: String,25}2627impl Overview for AnotherCourse {}2829fn call_overview(item: &impl Overview) {30 println!("Overview {}", item.overview());31}3233fn call_overview_generic<T: Overview>(item: &T) {34 println!("Overview generic {}", item.overview());35}3637fn call_overviewT(item: &impl Overview, item1: &impl Overview) {38 println!("OverviewT {}", item.overview());39 println!("OverviewT {}", item1.overview());40}4142// 只允许传入同类型结构体的引用43fn call_overviewTT<T: Overview>(item: &T, item1: &T) {44 println!("OverviewTT {}", item.overview());45 println!("OverviewTT {}", item1.overview());46}4748// 多绑定49fn call_mul_bind(item: &(impl Overview + Another)) {50 println!("Overview {}", item.overview());51 item.hell();52}5354// 多绑定泛型55fn call_mul_bind_generic<T>(item: &T)56where57 T: Overview + Another,58{59 println!("Overview {}", item.overview());60 item.hell();61}6263fn main() {64 let c0 = Course {65 headline: "ff".to_owned(),66 author: "yy".to_owned(),67 };68 let c1 = Course {69 headline: "ff".to_owned(),70 author: "yy".to_owned(),71 };7273 let c2 = AnotherCourse {74 headline: "ff".to_owned(),75 author: "yz".to_owned(),76 };77 call_overview(&c1); // Overview Course78 call_overview_generic(&c1); // Overview generic Course79 call_overviewT(&c1, &c2);80 // OverviewT Course81 // OverviewT Course82 call_overviewTT(&c0, &c1); // 应用范围宅一些83 // OverviewTT Course84 // OverviewTT Course85 call_overviewT(&c0, &c1);86 // OverviewT Course87 // OverviewT Course88 call_mul_bind(&c1);89 // Overview Course90 // welcome to hell91 call_mul_bind_generic(&c1);92 // Overview Course93 // welcome to hell94}
Overloading(Operator)
- 只需要实现相应的特质
为结构体实现一个加减的例子:
1use std::ops::Add;2use std::ops::Sub;3// 编译时4#[derive(Debug)]5struct Point<T> {6 x: T,7 y: T,8}910// T的这样类型,它可以执行相加的操作11impl<T> Add for Point <T>12 where T: Add<Output = T>13{14 type Output = Self;15 fn add(self, rhs: Self) -> Self::Output {16 Point {17 x: self.x + rhs.x,18 y: self.y + rhs.y,19 }20 }2122}2324// 相减25impl <T> Sub for Point <T>26 where T: Sub<Output = T>27{28 type Output = Self;29 fn sub(self, rhs: Self) -> Self::Output {30 Point {31 x: self.x - rhs.x,32 y: self.y - rhs.y,33 }34 }35}36fn main() {37 let i1 = Point{x: 1, y: 2};38 let i2 = Point{x: 1, y: 3};39 let sum = i1 + i2;40 println!("{:?}", sum); // Point { x: 2, y: 5 }4142 let i1 = Point{x: 1, y: 2};43 let i2 = Point{x: 1, y: 3};44 let sub = i1 - i2;45 println!("{:?}", sub); // Point { x: 0, y: -1 }46}
Trait & Polymorphism and inheritance
Rust并不支持传统的继承的概念,但是你可以在特质中通过层级化来完成你的需求。
Rust选择了一种函数式的编程范式,即“组合和委托”而非“继承”。
编程语言的大势也是组合优于继承。
例子:
1use std::collections::VecDeque;23// 多态4trait Driver {5 fn drive(&self);6}78struct Car;910impl Driver for Car {11 fn drive(&self) {12 println!("Car is driving")13 }14}15struct SUV;1617impl Driver for SUV {18 fn drive(&self) {19 println!("SUV is driving")20 }21}2223fn road(vehicle: &dyn Driver) {24 vehicle.drive()25}2627// 继承思想28// 单向特质29trait Queue {30 fn len(&self) -> usize;31 fn push_back(&mut self, n: i32);32 fn pop_front(&mut self) -> Option<i32>;33}3435// 双向特质36trait Deque: Queue {37 fn push_front(&mut self, n: i32);38 fn pop_back(&mut self) -> Option<i32>;39}4041#[derive(Debug)]42struct List {43 data: VecDeque<i32>,44}4546impl List {47 fn new() -> Self {48 let data = VecDeque::<i32>::new();49 Self{data}50 }51}5253impl Deque for List {54 fn push_front(&mut self, n: i32) {55 self.data.push_front(n)56 }5758 fn pop_back(&mut self) -> Option<i32> {59 self.data.pop_back()60 }61}6263impl Queue for List {64 fn len(&self) -> usize {65 self.data.len()66 }6768 fn pop_front(&mut self) -> Option<i32> {69 self.data.pop_back()70 }7172 fn push_back(&mut self, n: i32) {73 self.data.push_back(n)74 }75}7677fn main() {78 road(&Car); // Car is driving79 road(&SUV); // SUV is driving8081 let mut l = List::new();82 l.push_back(1);83 l.push_front(0);84 println!("{:?}", l); // List { data: [0, 1] }85 l.push_front(2);86 println!("{:?}", l); // List {data: [2, 0, 1] }87 l.push_back(3);88 println!("{:?}", l); // List {data: [2, 0, 1, 3]}89 println!("{}", l.pop_back().unwrap()); // 390 println!("{:?}", l); // List {data: [2, 0, 1] }91}
Common Type of Trait
DebugClonePartialEq
例如:
1// Clone Copy Debug PartialEq2// 层级 打个比方,结构体里面有个枚举,那么结构体实现Clone,里面的枚举也实现Clone34// 给枚举Race实现Debug,Clone5#[derive(Debug, Clone, Copy)]6enum Race {7 White,8 Yellow,9 Black,10}1112impl PartialEq for Race {13 fn eq(&self, other: &Self) -> bool {14 match(self, other) {15 (Race::White, Race::White) => true,16 (Race::Yellow, Race::Yellow) => true,17 (Race::Black, Race::Black) => true,18 _ => false,19 }20 }21}2223// 给结构体User实现Debug, Clone24#[derive(Debug, Clone)]25struct User {26 id: u32,27 name: String,28 race: Race,29}3031impl PartialEq for User {32 fn eq(&self, other: &Self) -> bool {33 self.id == other.id && self.name == other.name && self.race == other.race34 }35}3637fn main() {38 let user = User{39 id: 3,40 name: "John".to_owned(),41 race: Race::Yellow,42 };4344 println!("{:#?}", user);45 // User {46 // id: 3,47 // name: "John",48 // race: Yellow,49 // }50 let user2 = user.clone();51 println!("{:#?}", user2);52 // User {53 // id: 3,54 // name: "John",55 // race: Yellow,56 // }57 println!("{:#?}", user == user2); // true58}