当前位置: 首页 > news >正文

2024 Rust现代实用教程 Trait特质

文章目录

  • 一、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与多态和继承
  • 六、常见的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 runCompiling ch25_trait v0.1.0 (/home/wangji/installer/rust/project/ch25_trait)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.45sRunning `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 runCompiling ch26_trait_box v0.1.0 (/home/wangji/installer/rust/project/ch26_trait_box)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28sRunning `target/debug/ch26_trait_box`
Overview Obj
Obj
Overview Obj
pay 370
~/installer/rust/project/ch26_trait_box master 
 cargo runCompiling 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 defaultwarning: `ch26_trait_box` (bin "ch26_trait_box") generated 1 warningFinished `dev` profile [unoptimized + debuginfo] target(s) in 6.28sRunning `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)
whereT: 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 defaultwarning: 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 defaultwarning: 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 defaultwarning: 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 warningsFinished `dev` profile [unoptimized + debuginfo] target(s) in 0.00sRunning `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>
whereT: 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 runCompiling ch1_add v0.1.0 (/home/wangji/code/rust/2024_rust/rust_project/ch1_add)Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.09sRunning `target/debug/ch1_add`
Point { x: 2, y: 5 }
Point { x: 2.0, y: 5.2 }

五、Trait与多态和继承

六、常见的Trait

参考

  • 2024 Rust现代实用教程

http://www.mrgr.cn/news/64226.html

相关文章:

  • 记录新建wordpress站的实践踩坑:wordpress 上传源码新建站因权限问题导致无法访问、配置新站建站向导以及插件主题上传配置的解决办法
  • 2-137 基于matlab的sigmoid函数的变步长自适应语音信号增强
  • Tomcat 11 下载/安装 与基本使用
  • Git进阶(十七):特性分支
  • 蓝牙 BLE 详解
  • 从0开始学习shell脚本
  • Docker:namespace隔离实战
  • 模板注入代码执行漏洞
  • 前端三件套(HTML + CSS + JS)
  • 为什么大家都在学数字孪生呢?
  • Ubuntu删除docker
  • DataFlow v202410 版本更新 一站式数据处理平台
  • WPF中的CommandParameter如何使用
  • 今日 AI 简报|零样本视频生成、移动端轻量语言模型、自动驾驶多模态模型等前沿 AI 技术集中亮相
  • JavaScript本地存储的方式有哪些
  • Linux安装部署MinIO
  • 常见 CSS 选择器用法
  • 七、Go语言快速入门之函数func
  • RHCE的练习(10)
  • 【论文阅读】Associative Alignment for Few-shot Image Classification
  • 告别传统办公软件,这款编辑器让你事半功倍!
  • GEE 训练教程——ee.Image()的使用
  • 分拣线番茄分级缺陷识别图像分割系统:创新探讨教学
  • RHCE【SELinux】
  • Fake Location解除屏蔽分析
  • 《高频电子线路》—— 振荡器稳定性问题