锈 - 特性
trait告诉 Rust 编译器特定类型具有的功能,并且可以与其他类型共享。特征是不同类型之间共享行为的抽象定义。因此,我们可以说 trait 之于 Rust,就像接口之于Java或抽象类之于 C++。 trait 方法能够访问该 trait 中的其他方法。
特质定义
Trait 是通过提供 Trait 的名称后跟关键字“trait”来定义的。在定义任何 Trait 时,我们必须在 Trait 中提供方法声明。
定义特征:
pub trait Detail
{
fn Description(&self) -> i32;
fn years_since_launched(&self) -> i32;
}
在上面的示例中,我们定义了一个名为 Detail 的 Trait,并以 &self 作为参数声明了两个方法( Description ()、 years_since_launched ()),并将返回类型设置为 i32。因此,每当任何类型将实现此 Trait 时,都必须覆盖这两个方法并且必须定义它们的自定义主体。
特性实现:
Trait 实现类似于在其他语言(如Java或 c++)中实现接口。
这里我们使用关键字“impl”来实现一个Trait,然后写下我们必须实现的Trait's Name,然后我们使用关键字“for”来定义我们要为谁实现一个Trait。我们将为在impl块内的 Trait's Definition 中声明的所有方法定义方法体。
特性的实现:
impl trait_Name for type_Name {
///
method definitions
///
}
让我们了解一下traits例子的实现:
示例 1:
让我们在 Car 结构体上实现一个名为 Detail 的内置特征:
Rust
// Defining a Detail trait by defining the
// functionality it should include
pub trait Detail
{
fn description(&self) -> String;
fn years_since_launched(&self) -> i32;
}
struct Car
{
brand_name : String,
color: String,
launched_year : i32
}
// Implementing an in-built trait Detail
// on the Car struct
impl Detail for Car {
// Method returns an overview of the car
fn description(&self) -> String{
return format!("I have a {} which is {} in color.",
self.brand_name, self.color);
}
// Method returns the number of years between
// the launched year of this car i.e.
// 2020 and the release year of the movie
fn years_since_launched(&self) -> i32{
return 2021 - self.launched_year;
}
}
fn main()
{
let car = Car{
brand_name: "WagonR".to_string(),
color: "Red".to_string(),
launched_year:1992
};
let car2 = Car{
brand_name: "Venue".to_string(),
color: "White".to_string(),
launched_year:1997
};
println!("{}", car.description());
println!("The car was released {} years ago.\n",
car.years_since_launched());
println!("{}", car.description());
println!("The car was released {} years ago.",
car.years_since_launched());
}
Rust
// Defining a Maths trait by defining
// the functionality it should include
pub trait Maths
{
fn area_of_rectangle(&self) -> i32;
fn perimeter_of_rectangle(&self) -> i32;
}
struct Parameter
{
l:i32,
b:i32
}
// Implementing an in-built trait Detail
// on the Parameter struct
impl Maths for Parameter {
// Method returns area of rectangle
fn area_of_rectangle(&self) -> i32{
return self.l*self.b ;
}
// Method returns the perimeter of rectangle
fn perimeter_of_rectangle(&self) -> i32{
return 2*(self.l+self.b);
}
}
fn main()
{
let para =Parameter{
l: 5,
b: 6
};
println!("The area of rectangle is {}.",
para.area_of_rectangle());
println!("The perimeter of the rectangle is {}.",
para.perimeter_of_rectangle());
}
Rust
struct SmartPointer {
data: String,
}
// implementing Drop trait
impl Drop for SmartPointer {
fn drop(&mut self) {
println!("Dropping SmartPointer with data `{}`!", self.data);
}
}
fn main() {
let _c = SmartPointer { data: String::from("my stuff") };
let _d = SmartPointer { data: String::from("other stuff") };
println!("SmartPointers created.");
}
Rust
trait Clone: Sized {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) {
*self = source.clone()
}
}
Rust
trait MyDebug : std::fmt::Debug {
fn my_subtrait_function(&self);
}
Rust
struct Breakfast{}
struct Dinner{}
trait Food {
fn dish(&self) -> &'static str;
}
// Implement the `Food` trait for `breakfast`.
impl Food for Breakfast {
fn dish(&self) -> &'static str {
"bread-butter!"
}
}
// Implement the `Food` trait for `dinner`.
impl Food for Dinner {
fn dish(&self) -> &'static str {
"paneer-butter-masala!"
}
}
// Returns some struct that implements Food,
// but we don't know which one at compile time.
fn eat(n: i32) -> Box {
if n < 8 {
Box::new(Breakfast {})
} else {
Box::new(Dinner {})
}
}
fn main() {
let n = 3;
let food = eat(n);
println!("You have chosen a random dish for today {}",
food.dish());
}
Rust
#[derive(HelloWorld)]
struct Hello;
fn main() {
Hello::hello_world();
}
Rust
use std::ops::Add;
struct Value1(u32);
struct Value2(u32);
impl Add for Value1 {
type Output = Value1;
fn add(self, other: Value2) ->Value1 {
Value1(self.0 + (other.0 * 1000))
}
}
输出:
I have a WagonR which is Red in color.
The car was released 29 years ago.
I have a WagonR which is Red in color.
The car was released 29 years ago.
示例 2:
让我们在 Parameter 结构上实现一个名为 Maths 的内置特征:
锈
// Defining a Maths trait by defining
// the functionality it should include
pub trait Maths
{
fn area_of_rectangle(&self) -> i32;
fn perimeter_of_rectangle(&self) -> i32;
}
struct Parameter
{
l:i32,
b:i32
}
// Implementing an in-built trait Detail
// on the Parameter struct
impl Maths for Parameter {
// Method returns area of rectangle
fn area_of_rectangle(&self) -> i32{
return self.l*self.b ;
}
// Method returns the perimeter of rectangle
fn perimeter_of_rectangle(&self) -> i32{
return 2*(self.l+self.b);
}
}
fn main()
{
let para =Parameter{
l: 5,
b: 6
};
println!("The area of rectangle is {}.",
para.area_of_rectangle());
println!("The perimeter of the rectangle is {}.",
para.perimeter_of_rectangle());
}
输出:
The area of rectangle is 30.
The perimeter of the rectangle is 22.
掉落特性:
Drop trait 对智能指针模式很重要。 Drop trait 让我们可以自定义当一个值即将超出范围时会发生什么。 Drop trait 功能几乎总是在实现智能指针时使用。例如, Box
例子:
锈
struct SmartPointer {
data: String,
}
// implementing Drop trait
impl Drop for SmartPointer {
fn drop(&mut self) {
println!("Dropping SmartPointer with data `{}`!", self.data);
}
}
fn main() {
let _c = SmartPointer { data: String::from("my stuff") };
let _d = SmartPointer { data: String::from("other stuff") };
println!("SmartPointers created.");
}
在上面的示例中,有一个SmartPointer 结构,其自定义功能是在实例超出范围时打印 Dropping SmartPointer 。
输出:
SmartPointers created.
Dropping SmartPointer with data `other stuff`!
Dropping SmartPointer with data `my stuff`!
迭代器特性:
Iterator trait 在集合(如数组)上实现迭代器。 Iterator trait 将每个迭代器类型与其产生的值类型相关联。
trait 只需要为下一个元素定义一个方法,它可以在impl块中手动定义(如在数组和范围中),它一次返回迭代器的一个项目(Option
克隆特性:
克隆特征适用于可以复制自己的类型。克隆定义如下:
锈
trait Clone: Sized {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) {
*self = source.clone()
}
}
clone 方法应该构造一个 self 的独立副本并返回它。由于方法的返回类型是 self 并且函数可能不会返回 unsized 的值,Clone trait 本身扩展了 Sized trait(要调整大小的 self 类型)
Rust 不会自动克隆值,但它允许您进行显式方法调用。像 Rc
超能力:
如果我们可能需要一个特征来使用另一个特征的功能。在这种情况下,我们需要依赖也正在实现的依赖特性。 Rust 有一种方法可以指定一个 trait 是另一个 trait 的扩展,这给了我们类似于其他语言中的子类化的东西。
要创建子特征,请指明它以与类型相同的方式实现超特征:
锈
trait MyDebug : std::fmt::Debug {
fn my_subtrait_function(&self);
}
使用 dyn 返回特征:
Rust 中的 trait 对象类似于Java或 C++ 中的对象。 trait 对象总是通过一个指针传递,并且有一个 vtable 以便可以动态调度方法。 VTable 是一种函数指针数组,包含了该类所有虚函数的地址。
trait 对象的类型使用 dyn Trait:
例如; &dyn Bar 或 Box
使用 trait 对象的函数:
fn f(b: &dyn Bar) -> usize
use std::fmt::Debug;
fn dyn_trait(n: u8) -> Box {
todo!()
}
dyn_trait函数可以返回任意数量的实现 Debug trait 的类型,甚至可以根据输入参数返回不同的类型。
单个变量、参数或返回值可以采用多种不同类型的值。但是虚拟分派往往会降低方法调用的速度。并且对象必须通过指针传递。
例子:
锈
struct Breakfast{}
struct Dinner{}
trait Food {
fn dish(&self) -> &'static str;
}
// Implement the `Food` trait for `breakfast`.
impl Food for Breakfast {
fn dish(&self) -> &'static str {
"bread-butter!"
}
}
// Implement the `Food` trait for `dinner`.
impl Food for Dinner {
fn dish(&self) -> &'static str {
"paneer-butter-masala!"
}
}
// Returns some struct that implements Food,
// but we don't know which one at compile time.
fn eat(n: i32) -> Box {
if n < 8 {
Box::new(Breakfast {})
} else {
Box::new(Dinner {})
}
}
fn main() {
let n = 3;
let food = eat(n);
println!("You have chosen a random dish for today {}",
food.dish());
}
输出:
You have chosen a random dish for today bread-butter!
#[派生] 的概念:
#[derive] 属性可以被编译器用来提供一些traits的基本实现。如果需要更复杂的行为,可以手动实现traits。
以下是可推导的特征列表:Parameters Description Comparison traits Eq, PartialEq, Ord, PartialOrd Clone traits To create T from &T copy traits To give a type ‘copy semantics’ Serialization Encodable, Decodable Rand To create a random instance of a data type Hash traits To compute a hash from &T Zero To create a zero instance of a numeric data type FromPrimitive To create an instance from a numeric primitive Default traits To create an empty instance of a data type Debug traits To format a value using the {:?} formatter
看看下面的代码:
锈
#[derive(HelloWorld)]
struct Hello;
fn main() {
Hello::hello_world();
}
派生属性允许为数据结构自动生成新项目。它使用 MetaListPaths 语法来指定要实现的特征列表或导出要处理的宏的路径。
通过特征重载运算符:
运算符重载是在特定情况下自定义运算符的行为。我们不能创建自己的运算符,但我们可以通过实现特征重载 std::ops 中列出的操作和相应的特征。
锈
use std::ops::Add;
struct Value1(u32);
struct Value2(u32);
impl Add for Value1 {
type Output = Value1;
fn add(self, other: Value2) ->Value1 {
Value1(self.0 + (other.0 * 1000))
}
}