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 所需的代码。