跳到主要内容

Rust特性

提示
  1. 特性定义与实现:Rust的trait定义共享功能,类似于其他语言的接口。使用trait关键字定义,其中包含方法签名,然后在类型上使用impl关键字实现这些方法。
  2. 使用trait的泛型函数:可通过泛型函数使用trait,该函数可以接受实现了特定trait的任何类型。例如,print_thing<T: Printable>(thing: &T)可以打印任何实现了Printable trait的对象。
  3. trait的默认实现与derive关键字:trait可以包含默认实现的方法,减少重复代码。derive关键字自动生成某些trait的实现,如CopyClone,简化代码。

Rust trait 定义了多个类型的共享功能。

Rust traits 促进类型安全,防止编译时出错,并且像其他语言中的接口一样行为,但有一些区别。

在 Rust 中定义 Trait

我们可以使用 trait 关键字,后跟 trait 名称和该 trait 中的方法,来定义一个 Rust trait。

让我们看看 trait 的语法。

trait TraitName {
fn method_one(&self, [arguments: argument_type]) -> return_type;
fn method_two(&mut self, [arguments: argument_type]) -> return_type;
...
}

这里,

  • TraitName - trait 的名称。
  • method_one()method_two() - trait 中的方法名称。
  • &self&mut self - 对 self 值的引用。方法可以根据是否需要修改其值,接受对当前对象的可变或不可变引用。
  • [arguments: argument_type] (可选)- 参数列表,每个参数都有名称和类型。
  • return_type - 方法返回的类型。

现在,让我们定义一个 trait。

trait MyTrait {
fn method_one(&self);
fn method_two(&mut self, arg: i32) -> bool;
}

这里,我们声明了一个名为 MyTrait 的 trait,其中包含 method_one(&self)method_two(&mut self, arg: i32) -> bool 的方法签名。这些方法签名描述了实现此 trait 的类型的行为。

一个 trait 可以在其主体中有多个方法签名,每行一个。默认情况下,traits 什么也不做,只是定义。要使用 trait,某个类型需要实现它。

在 Rust 中实现 Trait

要实现一个 trait,我们使用 impl 关键字。实现(impl)块的语法是:

impl TraitName for TypeName {
fn method_one(&self, [arguments: argument_type]) -> return_type {
// method_one 的实现
}

fn method_two(&mut self, [arguments: argument_type]) -> return_type {
// method_two 的实现
}

...
}

这里,TraitName 是正在实现的 trait 的名称,TypeName 是实现该 trait 的类型的名称。

注意: 实现 trait 必须具有与 trait 中方法相同的签名,包括名称、参数类型和返回类型。

现在,让我们实现这个 trait。我们将使用 MyTrait 作为 trait 和 MyStruct 作为实现该 trait 的类型。

trait MyTrait {
// 方法签名
fn method_one(&self);
fn method_two(&mut self, arg: i32) -> bool;
}

struct MyStruct {
value: i32,
}

impl MyTrait for MyStruct {
// method_one 的实现
fn method_one(&self) {
println!("值为: {}", self.value);
}

// method_two 的实现
fn method_two(&mut self, arg: i32) -> bool {
if arg > 0 {
self.value += arg;
return true;
} else {
return false;
}
}
}

在这个示例中,

  • method_one() 获取自身的引用,并打印它的 self.value 字段值。
  • method_two() 获取自身的可变引用以及类型为 i32 的参数 arg。如果 arg 大于零,我们将 arg 加到 value 字段并返回 true,否则返回 false

示例:在 Rust 中定义、实现和使用 Trait

// 定义一个 trait Printable
trait Printable {
fn print(&self);
}

// 定义一个结构体来实现 trait
struct Person {
name: String,
age: u32,
}

// 在结构体 Person 上实现 trait Printable
impl Printable for Person {
fn print(&self) {
println!("Person {{ name: {}, age: {} }}", self.name, self.age);
}
}

// 定义另一个结构体来实现 trait
struct Car {
make: String,
model: String,
}

// 在结构体 Car 上定义 trait Printable
impl Printable for Car {
fn print(&self) {
println!("Car {{ make: {}, model: {} }}", self.make, self.model);
}
}

// 用于打印实现了 Printable trait 的任何对象的实用函数
fn print_thing<T: Printable>(thing: &T) {
thing.print();
}

fn main() {
// 实例化 Person 和 Car
let person = Person { name: "Hari".to_string(), age: 31 };
let car = Car { make: "Tesla".to_string(), model: "Model X".to_string() };

// 使用 Person 和 Car 的引用调用 print_thing 函数
print_thing(&person);
print_thing(&car);
}

输出

Person { name: Hari, age: 31 }
Car { make: Tesla, model: Model X }

在这个示例中,我们定义了一个 Printable trait,并为两个结构体 PersonCar 实现了它。Printable trait 要求实现者必须有 print 方法名称。

main() 函数中,我们实例化了 PersonCar,并将它们传递给 print_thing() 函数。print_thing 是一个泛型函数,可以接受实现了 Printable trait 的任何对象的引用。

要了解更多关于 Rust 中的泛型,请访问 Rust 泛型

Rust 中 Trait 的默认实现

有时为 trait 中的某些或所有方法提供默认行为是很有用的。在定义 Rust trait 时,我们还可以为方法定义默认实现。 例如,

trait MyTrait {
// 带有默认实现的方法
fn method_one(&self) {
println!("Inside method_one");
}

// 没有默认实现的方法
fn method_two(&self, arg: i32) -> bool;
}

这里,method_one() 中有一个 println!() 函数调用,作为实现了 MyTrait 的所有类型的默认行为。

然而,method_two() 只定义了方法签名。

Rust 中的 derive 关键字

Rust 中的 derive 关键字用于为某个类型生成某些特征的实现。它可以用在 structenum 定义中。

让我们看一个例子,

// 使用 derive 关键字生成 Copy 和 Clone 的实现
#[derive(Copy, Clone)]
struct MyStruct {
value: i32,
}

fn main() {
let x = MyStruct { value: 10 };
let y = x;

println!("x: {:?}", x.value);
println!("y: {:?}", y.value);
}

输出

x = 10
y = 10

这里,我们使用 derive 关键字实现了 Rust 标准库中的 CopyClone 特征。

Copy 特征允许我们通过简单复制来将 x 赋值给 yClone 特征允许我们创建一个新实例,该实例是现有实例的精确副本。

通过使用 derive 关键字,我们可以避免编写实现这些特征所需的代码。