📜  锈 - 特性

📅  最后修改于: 2022-05-13 01:55:23.516000             🧑  作者: Mango

锈 - 特性

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 自定义 Drop 以释放盒子指向的堆上的空间。

例子:

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),next 将只要有元素就返回 Some(Item) ,迭代结束时返回 None 表示迭代完成。



克隆特性:

克隆特征适用于可以复制自己的类型。克隆定义如下:

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 和 Arc 这样的引用计数指针类型是个例外:克隆其中之一只会增加引用计数并为您提供一个新指针。

超能力:

如果我们可能需要一个特征来使用另一个特征的功能。在这种情况下,我们需要依赖也正在实现的依赖特性。 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。

以下是可推导的特征列表:

ParametersDescription
Comparison traitsEq, PartialEq, Ord, PartialOrd
Clone traitsTo create T from &T
copy traitsTo give a type ‘copy semantics’ 
SerializationEncodable, Decodable
RandTo create a random instance of a data type
Hash traitsTo compute a hash from &T
ZeroTo create a zero instance of a numeric data type
FromPrimitiveTo create an instance from a numeric primitive
Default traitsTo 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))
 }
}