文章目录
- 一、Trait特质
- 二、Trait Object与Box
- 1.Trait Object
- 2.dyn关键字
- 3.Rust中数据传输的三种形式
- 4.特质与Box
- 三、Trait Object与泛型
- 1.泛型与impl不同的写法
- 2.Multiple Trait Bounds
- 四、重载操作符Operator
- 1.Rust重载操作符
- 2.为结构体实现一个加号的例子
- 五、Trait与多态和继承
- 1.Rust并不支持面向对象
- 2.多态
- 六、常见的Trait
- 参考
一、Trait特质
在Rust中,特质(Traits)是一种定义方法签名的机制
- 特质允许你定义一组方法的签名,但可以不提供具体的实现(也可以提供)。这些方法签名可以包括参数和返回类型,但可以不包括方法的实现代码。
任何类型都可以实现特质,只要它们提供了特质中定义的所有方法。这使得你可以为不同类型提供相同的行为。
特点:
- 1.内置常量:特质可以内置常量(const),特质中定义的常量在程序的整个生命周期内都是有效的。
- 2.默认实现:特质可以提供默认的方法实现。如果类型没有为特质中的某个方法提供自定义实现,将会使用默认实现。
- 3.多重实现:类型可以实现多个特质,这允许你将不同的行为组合在一起。
- 4.特质边界:在泛型代码中,你可以使用特质作为类型约束。这被称为特质边界,它限制了泛型类型必须实现的特质。
- 5.TraitAlias:Rust还支持traitalias,允许你为复杂的trait组合创建简洁的别名,以便在代码中更轻松地引l用。
Example:
trait Greeter {
fn greet(&self);
fn hello();
}
struct Person {
name: String,
}
impl Greeter for Person {
// 为Person 实现greet(&self)
fn greet(&self) {
println!("greet {}", self.name);
}
fn hello() {
println!("hello");
}
}
fn main() {
let person = Person {
name: "Yz".to_owned(),
};
person.greet();
Person::hello();
}
编译并运行
cargo run
Compiling ch25_trait v0.1.0 (/home/wangji/installer/rust/project/ch25_trait)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.45s
Running `target/debug/ch25_trait`
greet Yz
hello
二、Trait Object与Box
1.Trait Object
在运行时动态分配的对象
- “运行时泛型”
- 比泛型要灵活的多
2可以在集合中混入不同的类型对象
- 更容易处理相似的数据
3.有一些小小的性能损耗
2.dyn关键字
dyn 是 Rust中的关键字,用于声明特质对象(trait object)的类型。特质对象是实现了特定特质(trait)的类型的实例,但其具体类型在编译时是未知的。
因此,为了让编译器知道我们正在处理的是特质对象,我们需要在特质名称前面加上 dyn 关键字。
dyn关键字的作用是指示编译器处理特质对象。
3.Rust中数据传输的三种形式
不可变引l用(Immutable References)
- &dyn Trait
可变引l用 (Mutable References)
- &mut dyn Trait
Move语义所有权转移
- 特质需要用Box<dyn Trait>实现move,如果你需要在函数调用之间传递特质的所有权,并且希望避免在栈上分配大量的内存,可以使用 Box<dyn Trait>。
4.特质与Box
创建trait Object的三种方式
第一种
let o = Object{};
let o_obj: &dyn Object = &o;
第二种
let o_obj: &dyn Object = &0bject{};
第三种
let o_obj: Box<dyn Object> = Box::new(Object {}) ;
第一种和第二种都是创建不可变引用
第三种最常用也最灵活,一般来说会使用Box和特质来组成集合元素
// trait 不可变引用 \ Move
struct Obj {}
trait Overview {
fn overview(&self) -> String {
String::from("overview")
}
}
impl Overview for Obj {
fn overview(&self) -> String {
String::from("Obj")
}
}
// 不可变引用
fn call_obj(item: &impl Overview) {
println!("Overview {}", item.overview());
}
// Move
fn call_obj_box(item: Box<dyn Overview>) {
println!("Overview {}", item.overview());
}
trait Sale {
fn amount(&self) -> f64;
}
// 元组结构体
struct Common(f64);
impl Sale for Common {
fn amount(&self) -> f64 {
self.0
}
}
struct TenDiscount(f64);
impl Sale for TenDiscount {
fn amount(&self) -> f64 {
self.0 - 10.0
}
}
struct TenPercentDiscount(f64);
impl Sale for TenPercentDiscount {
fn amount(&self) -> f64 {
self.0 * 0.9
}
}
fn calculate(sales: &Vec<Box<dyn Sale>>) -> f64 {
sales.iter().map(|sale| sale.amount()).sum()
}
fn main() {
let a = Obj {};
call_obj(&a);
println!("{}", a.overview());
let b_a = Box::new(Obj {});
call_obj_box(b_a);
// println!("{}", b_a.overview());
/**
等价写法
let c: = Box::new(Common(100.0));
let t1: = Box::new(TenDiscount(100.0));
let t2: Vec<Box<dyn Sale>> = Box::new(TenPercentDiscount(200.0));
let sales: = vec![c, t1, t2]; // : Vec<Box<dyn Sale>>
*/
let c: Box<dyn Sale> = Box::new(Common(100.0));
let t1: Box<dyn Sale> = Box::new(TenDiscount(100.0));
let t2: Box<dyn Sale> = Box::new(TenPercentDiscount(200.0));
let sales = vec![c, t1, t2]; // : Vec<Box<dyn Sale>>
println!("pay {}", calculate(&sales));
}
编译及运行:
cargo run
Compiling ch26_trait_box v0.1.0 (/home/wangji/installer/rust/project/ch26_trait_box)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/ch26_trait_box`
Overview Obj
Obj
Overview Obj
pay 370
~/installer/rust/project/ch26_trait_box master
cargo run
Compiling ch26_trait_box v0.1.0 (/home/wangji/installer/rust/project/ch26_trait_box)
warning: unused doc comment
--> src/main.rs:61:5
|
61 | / /**
62 | | 等价写法
63 | | let c: Box<dyn Sale> = Box::new(Common(100.0));
64 | | let t1: Box<dyn Sale> = Box::new(TenDiscount(100.0));
... |
68 | |
69 | | */
| |_______^
70 | let c: Box<dyn Sale> = Box::new(Common(100.0));
| ----------------------------------------------- rustdoc does not generate documentation for statements
|
= help: use `/* */` for a plain comment
= note: `#[warn(unused_doc_comments)]` on by default
warning: `ch26_trait_box` (bin "ch26_trait_box") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 6.28s
Running `target/debug/ch26_trait_box`
Overview Obj
Obj
Overview Obj
pay 370
三、Trait Object与泛型
1.泛型与impl不同的写法
fn call(item1: &impl Trait, item2: &impl Trait) ;
可以是不同类型
fn call_generic<T: Trait>(item1: &T, item2: &T) ;
必须是相同类型
2.Multiple Trait Bounds
fn call(item1: & (impl Trait+ AnotherTrait) );
fn call_generic<T: Trait+ AnotherTrait>(item1: &T);
Example:
trait Overview {
fn overview(&self) -> String {
String::from("Course")
}
}
trait Another {
fn hell(&self) {
println!("welcome to hell");
}
}
struct Course {
headline: String,
author: String,
}
impl Overview for Course {}
impl Another for Course {}
struct AnotherCourse {
headline: String,
author: String,
}
impl Overview for AnotherCourse {}
// 写法1:入参类型:&impl Overview
fn call_overview(item: &impl Overview) {
println!("Overview {}", item.overview());
}
// 写法2:指定模板参数是T: Overview
fn call_overview_generic<T: Overview>(item: &T) {
println!("Overview {}", item.overview());
}
fn call_overviewT(item: &impl Overview, item1: &impl Overview) {
println!("Overview {}", item.overview());
println!("Overview {}", item1.overview());
}
fn call_overviewTT<T: Overview>(item: &T, item1: &T) {
println!("Overview {}", item.overview());
println!("Overview {}", item1.overview());
}
// 多绑定写法1
fn call_mul_bind(item: &(impl Overview + Another)) {
println!("Overview {}", item.overview());
item.hell();
}
// 多绑定写法2
fn call_mul_bind_generic<T: Overview + Another>(item: &T) {
println!("Overview {}", item.overview());
item.hell();
}
// 多绑定写法3
fn call_mul_bind_generic_generic<T>(item: &T)
where
T: Overview + Another,
{
println!("Overview {}", item.overview());
item.hell();
}
fn main() {
let c0 = Course {
headline: "ff".to_owned(),
author: "yy".to_owned(),
};
let c1 = Course {
headline: "ff".to_owned(),
author: "yy".to_owned(),
};
let c2 = AnotherCourse {
headline: "ff".to_owned(),
author: "yz".to_owned(),
};
// call_overview(&c1);
// call_overview_generic(&c1);
// call_overviewT(&c1, &c2);
// println!("-------------------");
// call_overviewTT(&c1, &c0);
// call_overviewT(&c1, &c0);
call_mul_bind(&c1);
call_mul_bind_generic(&c1);
}
编译及运行:
cargo run
warning: unused variable: `c0`
--> src/main.rs:69:9
|
69 | let c0 = Course {
| ^^ help: if this is intentional, prefix it with an underscore: `_c0`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `c2`
--> src/main.rs:78:9
|
78 | let c2 = AnotherCourse {
| ^^ help: if this is intentional, prefix it with an underscore: `_c2`
warning: fields `headline` and `author` are never read
--> src/main.rs:14:5
|
13 | struct Course {
| ------ fields in this struct
14 | headline: String,
| ^^^^^^^^
15 | author: String,
| ^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: fields `headline` and `author` are never read
--> src/main.rs:22:5
|
21 | struct AnotherCourse {
| ------------- fields in this struct
22 | headline: String,
| ^^^^^^^^
23 | author: String,
| ^^^^^^
warning: function `call_overview` is never used
--> src/main.rs:29:4
|
29 | fn call_overview(item: &impl Overview) {
| ^^^^^^^^^^^^^
warning: function `call_overview_generic` is never used
--> src/main.rs:34:4
|
34 | fn call_overview_generic<T: Overview>(item: &T) {
| ^^^^^^^^^^^^^^^^^^^^^
warning: function `call_overviewT` is never used
--> src/main.rs:38:4
|
38 | fn call_overviewT(item: &impl Overview, item1: &impl Overview) {
| ^^^^^^^^^^^^^^
warning: function `call_overviewTT` is never used
--> src/main.rs:43:4
|
43 | fn call_overviewTT<T: Overview>(item: &T, item1: &T) {
| ^^^^^^^^^^^^^^^
warning: function `call_mul_bind_generic_generic` is never used
--> src/main.rs:60:4
|
60 | fn call_mul_bind_generic_generic<T>(item: &T)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: function `call_overviewT` should have a snake case name
--> src/main.rs:38:4
|
38 | fn call_overviewT(item: &impl Overview, item1: &impl Overview) {
| ^^^^^^^^^^^^^^ help: convert the identifier to snake case: `call_overview_t`
|
= note: `#[warn(non_snake_case)]` on by default
warning: function `call_overviewTT` should have a snake case name
--> src/main.rs:43:4
|
43 | fn call_overviewTT<T: Overview>(item: &T, item1: &T) {
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `call_overview_tt`
warning: `ch27_trait_generic` (bin "ch27_trait_generic") generated 11 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/ch27_trait_generic`
Overview Course
welcome to hell
Overview Course
welcome to hell
四、重载操作符Operator
1.Rust重载操作符
只需要实现相应的特质
2.为结构体实现一个加号的例子
use std::ops::Add;
// 编译时
//#[derive(Debug)] 实现Debug打印的特质
/**
* #[derive(Debug)]
* 等价于
* impl Debug for Point {...}
*
*
*/
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
// T的这样类型 它可以执行相加的操作
impl<T> Add for Point<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Point {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
fn main() {
let i1 = Point { x: 1, y: 2 };
let i2 = Point { x: 1, y: 3 };
let sum = i1 + i2;
println!("{:?}", sum);//#[derive(Debug)]
let f1 = Point { x: 1.0, y: 2.2 };
let f2 = Point { x: 1.0, y: 3.0 };
let sum = f1 + f2;
println!("{:?}", sum);
}
编译及运行
▶ cargo run
Compiling ch1_add v0.1.0 (/home/wangji/code/rust/2024_rust/rust_project/ch1_add)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.09s
Running `target/debug/ch1_add`
Point { x: 2, y: 5 }
Point { x: 2.0, y: 5.2 }
五、Trait与多态和继承
1.Rust并不支持面向对象
Rust并不支持传统的继承的概念,但是你可以在特质中通过层级化来完成你的需求
Rust选择了一种函数式的编程范式,即"组合和委托"而非"继承"
编程语言的大势也是组合优于继承
2.多态
多态是面向对象编程中的一个重要概念,指的是同一个方法调用可以根据对象的不同类型而表现出不同的行为。简单来说,多态允许一个接口或方法在不同的上下文中表现出不同的行为。这样做的好处是可以提高代码的灵活性和可扩展性,使得代码更易于维护和理解
Rust中的多态无处不在
use std::collections::VecDeque;
// 多态
trait Driver {
fn drive(&self);
}
struct Car;
impl Driver for Car {
fn drive(&self) {
println!("Car is driving");
}
}
struct SUV;
impl Driver for SUV {
fn drive(&self) {
println!("SUV is driving");
}
}
fn road(vehicle: &dyn Driver) {
vehicle.drive();
}
// 继承思想:针对于特质
// 单向特质
trait Queue {
fn len(&self) -> usize;
fn push_back(&mut self, n: i32);//后面加入
fn pop_front(&mut self) -> Option<i32>;//前面移除
}
// 双向特质
trait Deque: Queue {
fn push_front(&mut self, n: i32);//从前面加入
fn pop_back(&mut self) -> Option<i32>;//从后面移除
}
#[derive(Debug)]
struct List {
data: VecDeque<i32>,
}
impl List {
fn new() -> Self {
let data = VecDeque::<i32>::new();
Self { data }
}
}
impl Deque for List {
fn push_front(&mut self, n: i32) {
self.data.push_front(n)
}
fn pop_back(&mut self) -> Option<i32> {
self.data.pop_back()
}
}
impl Queue for List {
fn len(&self) -> usize {
self.data.len()
}
fn push_back(&mut self, n: i32) {
self.data.push_back(n)
}
fn pop_front(&mut self) -> Option<i32> {
self.data.pop_front()
}
}
fn main() {
// road(&Car);
// road(&SUV);
let mut l = List::new();
l.push_back(1);
l.push_front(0);
println!("{:?}", l);
l.push_front(2);
println!("{:?}", l);
l.push_back(2);
println!("{:?}", l);
println!("{}", l.pop_back().unwrap());
println!("{:?}", l);
}
编译及运行
▶ cargo run
Compiling ch2_trai v0.1.0 (/home/wangji/code/rust/2024_rust/rust_project/ch2_trai)
warning: trait `Driver` is never used
--> src/main.rs:3:7
|
3 | trait Driver {
| ^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: struct `Car` is never constructed
--> src/main.rs:6:8
|
6 | struct Car;
| ^^^
warning: struct `SUV` is never constructed
--> src/main.rs:13:8
|
13 | struct SUV;
| ^^^
warning: function `road` is never used
--> src/main.rs:20:4
|
20 | fn road(vehicle: &dyn Driver) {
| ^^^^
warning: methods `len` and `pop_front` are never used
--> src/main.rs:26:8
|
25 | trait Queue {
| ----- methods in this trait
26 | fn len(&self) -> usize;
| ^^^
27 | fn push_back(&mut self, n: i32);
28 | fn pop_front(&mut self) -> Option<i32>;
| ^^^^^^^^^
warning: `ch2_trai` (bin "ch2_trai") generated 5 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.61s
Running `target/debug/ch2_trai`
List { data: [0, 1] }
List { data: [2, 0, 1] }
List { data: [2, 0, 1, 2] }
2
List { data: [2, 0, 1] }
六、常见的Trait
// 常见的trait:Debug Clone Copy PartialEq
// 层级
/**
* #[derive(Debug)]等价于
* impl Debug for XXX {}
*
*
* 特质Copy表示能够拷贝,而不是移动
*/
#[derive(Debug, Clone, Copy)]
enum Race {
White,
Yellow,
Black,
}
impl PartialEq for Race {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Race::White, Race::White) => true,
(Race::Yellow, Race::Yellow) => true,
(Race::Black, Race::Black) => true,
_ => false,
}
}
}
#[derive(Debug, Clone)]
struct User {
id: u32,
name: String,
race: Race,
}
impl PartialEq for User {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.name == other.name && self.race == other.race
}
}
fn main() {
let user = User {
id: 3,
name: "John".to_owned(),
race: Race::Yellow,
};
println!("{:?}", user);//enum Race和struct User都得实现Debug特质,才能打印出来
let user2 = user.clone();//Clone特质
println!("{:#?}", user2);
println!("{}", user == user2);
}
编译及运行:
▶ cargo run
Compiling ch63_trait_common v0.1.0 (/home/wangji/code/rust/2024_rust/rust_project/ch3_trait_common)
warning: variants `White` and `Black` are never constructed
--> src/main.rs:5:5
|
4 | enum Race {
| ---- variants in this enum
5 | White,
| ^^^^^
6 | Yellow,
7 | Black,
| ^^^^^
|
= note: `Race` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: `ch63_trait_common` (bin "ch63_trait_common") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
Running `target/debug/ch63_trait_common`
User { id: 3, name: "John", race: Yellow }
User {
id: 3,
name: "John",
race: Yellow,
}
参考
- 2024 Rust现代实用教程