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 可以在其主体中包含多个方法签名,每行一个。Trait 默认不执行任何操作,仅为定义。为了使用 trait,类型需要实现它。
在 Rust 中实现 Trait
要实现 trait,我们使用 impl 关键字。实现 (impl) 块的语法是
impl TraitName for TypeName {
fn method_one(&self, [arguments: argument_type]) -> return_type {
// implementation for method_one
}
fn method_two(&mut self, [arguments: argument_type]) -> return_type {
// implementation for method_two
}
...
}
这里,TraitName 是正在实现的 trait 的名称,TypeName 是正在实现该 trait 的类型的名称。
注意: Trait 的实现必须具有与 trait 中的方法相同的签名,包括名称、参数类型和返回类型。
现在,让我们实现 trait。我们将使用 MyTrait 作为 trait,并将 MyStruct 作为我们为其实现 trait 的类型。
trait MyTrait {
// method signatures
fn method_one(&self);
fn method_two(&mut self, arg: i32) -> bool;
}
struct MyStruct {
value: i32,
}
impl MyTrait for MyStruct {
// implementation of method_one
fn method_one(&self) {
println!("The value is: {}", self.value);
}
// implementation of method_two
fn method_two(&mut self, arg: i32) -> bool {
if arg > 0 {
self.value += arg;
return true;
} else {
return false;
}
}
}
在此示例中,
method_one()接受对 self 的引用,并打印其self.value字段的值。method_two()接受对 self 的可变引用和类型为i32的参数arg。如果arg大于零,我们将arg添加到 value 字段并返回true,否则返回false。
示例:在 Rust 中定义、实现和使用 Trait
// Define a trait Printable
trait Printable {
fn print(&self);
}
// Define a struct to implement a trait
struct Person {
name: String,
age: u32,
}
// Implement trait Printable on struct Person
impl Printable for Person {
fn print(&self) {
println!("Person {{ name: {}, age: {} }}", self.name, self.age);
}
}
// Define another struct to implement a trait
struct Car {
make: String,
model: String,
}
// Define trait Printable on struct Car
impl Printable for Car {
fn print(&self) {
println!("Car {{ make: {}, model: {} }}", self.make, self.model);
}
}
// Utility function to print any object that implements the Printable trait
fn print_thing<T: Printable>(thing: &T) {
thing.print();
}
fn main() {
// Instantiate Person and Car
let person = Person { name: "Hari".to_string(), age: 31 };
let car = Car { make: "Tesla".to_string(), model: "Model X".to_string() };
// Call print_thing with reference of Person and Car
print_thing(&person);
print_thing(&car);
}
输出
Person { name: Hari, age: 31 }
Car { make: Tesla, model: Model X }
在此示例中,我们定义了一个 Printable trait,并为两个结构体 Person 和 Car 实现了它。Printable trait 要求实现者具有名为 print 的方法。
在 main() 函数中,我们实例化 Person 和 Car,并将它们传递给 print_thing() 函数。print_thing 是一个泛型函数,它可以接受实现了 Printable trait 的任何对象的引用。
要了解有关 Rust 中泛型的更多信息,请访问 Rust Generics。
在 Rust 中 Trait 的默认实现
有时为 trait 中的某些或所有方法提供默认行为会很有用。在定义 Rust trait 时,我们也可以为方法提供默认实现。
例如,
trait MyTrait {
// method with a default implementation
fn method_one(&self) {
println!("Inside method_one");
}
// method without a default implementation
fn method_two(&self, arg: i32) -> bool;
}
在这里,method_one() 在 method_one() 主体中有一个 println!() 函数调用,它充当实现 trait MyTrait 的所有类型的默认行为。
但是,method_two() 只定义了方法签名。
Rust 中的 derive 关键字
Rust 中的 derive 关键字用于为类型生成某些 trait 的实现。它可以在 struct 或 enum 定义中使用。
我们来看一个例子:
// use derive keyword to generate implementations of Copy and 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 标准库中的 Copy 和 Clone trait。
Copy trait 允许我们通过简单复制将 x 赋值给 y。Clone trait 允许我们创建现有实例的精确副本的新实例。
通过使用 derive 关键字,我们可以避免编写实现这些 trait 所需的代码。