2024 Rust现代实用教程 Trait特质

文章目录

一、Trait特质

在Rust中,特质(Traits)是一种定义方法签名的机制

  • 特质允许你定义一组方法的签名,但可以不提供具体的实现(也可以提供)。这些方法签名可以包括参数和返回类型,但可以不包括方法的实现代码。

任何类型都可以实现特质,只要它们提供了特质中定义的所有方法。这使得你可以为不同类型提供相同的行为。

特点:

  • 1.内置常量:特质可以内置常量(const),特质中定义的常量在程序的整个生命周期内都是有效的。
  • 2.默认实现:特质可以提供默认的方法实现。如果类型没有为特质中的某个方法提供自定义实现,将会使用默认实现。
  • 3.多重实现:类型可以实现多个特质,这允许你将不同的行为组合在一起。
  • 4.特质边界:在泛型代码中,你可以使用特质作为类型约束。这被称为特质边界,它限制了泛型类型必须实现的特质。
  • 5.TraitAlias:Rust还支持traitalias,允许你为复杂的trait组合创建简洁的别名,以便在代码中更轻松地引l用。

Example:

rust 复制代码
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();
}

编译并运行

bash 复制代码
 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的三种方式

第一种

rust 复制代码
let o = Object{};
let o_obj: &dyn Object = &o;

第二种

rust 复制代码
let o_obj: &dyn Object = &0bject{};

第三种

rust 复制代码
let o_obj: Box<dyn Object> = Box::new(Object {}) ;

第一种和第二种都是创建不可变引用

第三种最常用也最灵活,一般来说会使用Box和特质来组成集合元素

rust 复制代码
// 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));
}

编译及运行:

bash 复制代码
 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不同的写法

rust 复制代码
fn call(item1: &impl Trait, item2: &impl Trait) ;
可以是不同类型

fn call_generic<T: Trait>(item1: &T, item2: &T) ;
必须是相同类型

2.Multiple Trait Bounds

rust 复制代码
fn call(item1: & (impl Trait+ AnotherTrait) );
fn call_generic<T: Trait+ AnotherTrait>(item1: &T);

Example:

rust 复制代码
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);
}

编译及运行:

bash 复制代码
 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

五、Trait与多态和继承

六、常见的Trait

参考

相关推荐
struggle20257 小时前
Burn 开源程序是下一代深度学习框架,在灵活性、效率和可移植性方面毫不妥协
人工智能·python·深度学习·rust
泯泷11 小时前
「译」为 Rust 及所有语言优化 WebAssembly
前端·后端·rust
寻月隐君18 小时前
Solana 开发实战:Rust 客户端调用链上程序全流程
后端·rust·web3
UestcXiye2 天前
Rust 学习笔记:Stream
rust
受之以蒙2 天前
Rust+Wasm利器:用wasm-pack引爆前端性能!
前端·rust·webassembly
UestcXiye2 天前
Rust 学习笔记:关于处理任意数量的 future 的练习题
rust
无名之逆3 天前
大三自学笔记:探索Hyperlane框架的心路历程
java·开发语言·前端·spring boot·后端·rust·编程
susnm3 天前
RSX 构建界面
rust·全栈
维维酱3 天前
Rust - async/await
rust
asyncrustacean3 天前
有栈协程基本原理和实现
后端·rust·go