Trait

在Rust中,Trait(特质)是一种定义方法集合的机制,类似于其他编程语言中的接口(java)或抽象类(c++的虚函数)。

定义Trait

trait MyTrait { //关键字trait(线条、特征、勾勒)

// 定义Trait的方法

fn my_method(&self);//只有方法签名,没有具体实现,实现该trait的类型必须提供具体实现

// 可选:可以在这里定义其他方法

fn my_method_1(&self)-> String {

String::from("也可以提供默认实现")

};

}

实现Trait

struct MyStruct;

//实现trait用impl这个关键字,格式为impl trait_name for struct_name{}。impl另一个主要的用途是实现struct的“类成员方法”,格式为impl struct_name{}

impl MyTrait for MyStruct {

fn my_method(&self) {

println!("This is the implementation for my_method in MyStruct");

}

}

孤儿规则

可在某个类型上实现trait:

类型或者trait在当前作用域中定义的 无法为外部类型来实现外部的trait:

这个限制是程序属性的一部分(也就是一致性)。

更具体的说是孤儿规则:之所以这样命名是因为父类型不存在。

此规则确保其他人的代码不能破坏您的diamond,反之亦然。

如果没有这个规则,两个crate可以为同一个理性实现同一个trait,Rust就不知道该使用哪个实现了。

如果允许任意 crate 对任意类型实现任意 trait,可能会导致多个 crate 中的相同类型实现相同的 trait,这会引起冲突和不可预期的行为。

引入与使用Trait

简单示例:

// 定义一个 Trait

trait Animal {

fn make_sound(&self);

}

// 实现 Trait for struct Dog

struct Dog;

impl Animal for Dog {

fn make_sound(&self) {

println!("Woof!");

}

}

// 使用 Trait 对象

fn print_sound(a: &dyn Animal) {

a.make_sound();

}

fn main() {

let dog = Dog;

// 两种调用方法

dog.make_sound(); // 输出: Woof!

print_sound(&dog); // 输出: Woof!

}

将trait打包到库中

Trait作为参数

使用多个Trait

// 定义两个trait,一个“总结”,一个“展示”

trait Summary {

fn summarize(&self) -> String;

}

trait Display {

fn display(&self);

}

// 定义结构体 CCTV

struct CCTV {

author: String, content: String,

}

// 为CCTV实现总结功能

impl Summary for CCTV {

fn summarize(&self) -> String {

format!("{}, by {}", self.content, self.author)

}

}

// 为CCTV实现展示功能

impl Display for CCTV {

fn display(&self) {

println!("Author: {}", self.author);

println!("Content: {}", self.content);

}

}

// 定义一个函数,接受实现了 Summary 和 Display traits 的对象作为参数

pub fn notify(item: &(impl Summary + Display)) {

println!("Breaking news!");

println!("Summary: {}", item.summarize());

item.display();

}

fn main() {

let article = CCTV {

author: "Sports editor".to_string(),

content: "2024欧洲杯 聚焦绿茵盛宴!".to_string(),

};

notify(&article);

}

运行结果:

Breaking news!

Summary: 2024欧洲杯 聚焦绿茵盛宴!, by Sports editor

Author: Sports editor

Content: 2024欧洲杯 聚焦绿茵盛宴!

// 定义两个trait,一个“总结”,一个“展示”

trait Summary {

fn summarize(&self) -> String;

}

trait Display {

fn display(&self);

}

// 定义结构体 CCTV

struct CCTV {

author: String, content: String,

}

// 为CCTV实现总结功能

impl Summary for CCTV {

fn summarize(&self) -> String {

format!("{}, by {}", self.content, self.author)

}

}

// 为CCTV实现展示功能

impl Display for CCTV {

fn display(&self) {

println!("Author: {}", self.author);

println!("Content: {}", self.content);

}

}

// 定义Hacknews 结构体

struct Hacknews {

username: String,

content: String,

}

// 为Hacknews实现“展示功能”

impl Display for Hacknews {

fn display(&self) {

println!("Tweet by {}: {}", self.username, self.content);

}

}

// 定义一个函数,接受实现了 Summary 和 Display traits 的对象作为参数

pub fn notify(item: &(impl Summary + Display)) {

println!("Breaking news!");

println!("Summary: {}", item.summarize());

item.display();

}

fn main() {

let article = CCTV {

author: "Sports editor".to_string(),

content: "2024欧洲杯 聚焦绿茵盛宴!".to_string(),

};

let hack = Hacknews {

username: "Mako".to_string(),

content: "fast, production-grade web bundler based on Rust (makojs.dev) ! from https://makojs.dev/blog/mako-open-sourced".to_string(),

};

notify(&article);

// 需要多个trait都实现了才能运行

// notify(&hack); // 报错 the trait bound 'Tweet:summary' is not satisfied ,the trait 'summary' is implemented for 'NewsArticle'

}

泛型中使用Trait

// 定义两个Trait

trait Printable {

fn print(&self);

}

trait Drawable {

fn draw(&self);

}

// 实现这两个Trait的类型

struct Circle {

radius: f64,

}

impl Printable for Circle {

fn print(&self) {

println!("Circle with radius {}", self.radius);

}

}

impl Drawable for Circle {

fn draw(&self) {

println!("Drawing a circle with radius {}", self.radius);

}

}

// 函数接受实现了两个Trait的类型

fn print_and_draw(item: &T)

where

T: Printable + Drawable,

{

item.print();

item.draw();

}

fn main() {

let c = Circle { radius: 3.5 };

c.print(); // 输出: Circle with radius 3.5

c.draw(); // 输出: Drawing a circle with radius 3.5

print_and_draw(&c);

}

trait bound形式

从以上的例子中可以发现。在 Rust 中,有两种主要的方式来约束泛型类型参数必须实现特定的 trait(特性):trait bound 形式和 impl Trait 语法糖形式。让我们来详细比较它们: Trait Bound 形式:Trait bound 形式使用 where 子句来为泛型类型参数指定 trait 约束。例如:

fn example(item: &T) -> usize

where

T: Display + Clone,

{

// 函数体

}

这里 T: Display + Clone 表示泛型类型 T 必须同时实现 Display 和 Clone 这两个 trait。关键点在于 where 子句可以将多个 trait 约束放在一起,并且在使用泛型时可以非常清晰地看到这些约束。 impl Trait 语法糖形式

impl Trait 语法糖形式用于在函数签名中直接指定参数必须实现的一个或多个 trait。例如:

fn notify(item: &(impl Summary + Display)) {

// 函数体

}

这里 &(impl Summary + Display) 表示 item 参数必须实现 Summary 和 Display 这两个 trait。这种写法更加简洁和紧凑,适用于函数参数较少且只有少数几个约束的情况。

Ttait作为返回类型

#[derive(Trait)]

在定义一些struct往往会有#[derive(Debug)]标签。Debug trait 是 Rust 标准库提供的一个 trait,用于以调试输出的格式打印类型的实例。当一个结构体或枚举被标记为 #[derive(Debug)] 时,Rust 编译器会自动为这个类型生成实现 Debug trait 的代码。这使得我们可以通过使用 {:?} 或者 println!("{:?}", value) 等方式打印这个类型的实例。 #[derive(Debug)]

struct MyStruct {

field1: i32,

field2: String,

}

在这个例子中,MyStruct 结构体通过 #[derive(Debug)] 派生了 Debug trait。这样,我们就可以使用 println!("{:?}", my_struct_instance) 来打印 MyStruct 的实例。 更多相关 #[derive(Trait)]的内容可去了解一下过程宏,下面是一个例子,使得在主程序中可以使用宏 AnswerFn 来自动生成一个 answer() 函数,可以按照以下步骤进行:

第一步:创建 proc-macro crate

在一个新目录中创建一个 Cargo 项目:

cargo new proc-macro-examples --lib

将 Cargo.toml 文件中的内容更新为:

[package]

name = "proc-macro-examples"

version = "0.1.0"

edition = "2021"

[lib]

proc-macro = true

[dependencies]

# 没有依赖

更新 src/lib.rs 文件,添加 proc-macro 的实现:

#![crate_type = "proc-macro"]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_derive(AnswerFn)]

pub fn derive_answer_fn(_item: TokenStream) -> TokenStream {

"fn answer() -> u32 { 123 }".parse().unwrap()

}

第二步:创建使用宏的应用程序

接下来,创建一个使用 proc-macro crate 的 Rust 应用程序。这个应用程序将使用宏 AnswerFn 来为一个结构体自动生成 answer() 函数。

创建另一个新的 Rust 项目:

cargo new macro-app

更新 Cargo.toml 文件以依赖于我们刚刚创建的 proc-macro crate:

[package]

name = "macro-app"

version = "0.1.0"

edition = "2021"

[dependencies]

proc-macro-examples = { path = "../proc-macro-examples" }

更新 src/main.rs 文件,使用宏 AnswerFn:

extern crate proc_macro_examples;

use proc_macro_examples::AnswerFn;

#[derive(AnswerFn)]

struct Struct;

fn main() {

assert_eq!(123, answer());

}

编译和运行

先编译proc-macro-examples

cd proc-macro-examples

cargo build

然后,可以编译并运行应用程序:

cd macro-app

cargo run

运行结果

PS C:\Users\kingchuxing\Documents\learning-libp2p-main\macro-app> cargo run

warning: `macro-app` (bin "macro-app") generated 1 warning

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s

Running `target\debug\macro-app.exe`

PS C:\Users\kingchuxing\Documents\learning-libp2p-main\macro-app>

推荐文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。

大家都在找:

rust:rust腐蚀百科

学习:孩子学习态度不好怎么办

笔记:笔记本电脑排名前十

大家都在看: