📜  锈特质

📅  最后修改于: 2021-01-08 13:54:27             🧑  作者: Mango

锈性状

  • Rust特征是Rust语言的一种功能,描述了它可以提供的每种类型的功能。
  • 特质类似于其他语言中定义的接口的功能。
  • 特征是一种将方法签名分组以定义一组行为的方法。
  • 通过使用trait关键字定义特征。

特征的语法:

 trait trait_name
{
     //body of the trait.
}  

在上述情况下,我们声明特征后跟特征名称。在花括号内,声明了方法签名以描述实现特征的类型的行为。

让我们看一个简单的例子:

 struct Triangle
{
  base : f64,
  height : f64,
}
trait HasArea
{
  fn area(&self)->f64;
}

impl HasArea for Triangle
{
  fn area(&self)->f64
  {
    0.5*(self.base*self.height)
  }
}
fn main()
{
  let a = Triangle{base:10.5,height:17.4};
  let triangle_area = a.area();
  println!("Area of a triangle is {}",triangle_area); 
}

输出:

Area of a triangle is 91.35

在上面的示例中,声明了名为HasArea的特征,其中包含area()函数的声明。 HasArea在类型Triangle上实现。通过使用结构实例即a.area()可以简单地调用area()函数。

特质论证

性状还可以用作许多不同类型的参数。

上面的示例实现了HasArea特征,它包含area()函数的定义。我们可以定义调用area()函数的calculate_area()函数,并使用实现HasArea特性的类型的实例来调用area()函数。

让我们看一下语法:

 fn calculate_area(item : impl HasArea)
{
   println!("Area of the triangle is : {}",item.area());
 } 

泛型函数的特质界限

特性是有用的,因为它们描述了不同方法的行为。但是,泛型函数不遵循此约束。让我们通过一个简单的场景来理解这一点:

 fn calculate_area( item : T)
{
    println!(?Area of a triangle is {}?, item.area());
}

在上述情况下,Rust编译器会抛出“找不到名为T类型的方法的错误”。如果将特征绑定到通用T,则可以克服以下错误:

 fn calculate_area (item : T)
{
println!("Area of a triangle is {} ",item.area());


}

在上述情况下,表示“ T可以是实现HasArea特性的任何类型”。 Rust编译器知道,任何实现HasArea特征的类型都将具有area()函数。

让我们看一个简单的例子:

 trait HasArea
{
  fn area(&self)->f64;
}
struct Triangle
{
  base : f64,
  height : f64,
}

impl HasArea for Triangle
{
  fn area(&self)->f64
  {
    0.5*(self.base*self.height)
  }
}
struct Square
{
  side : f64,
}

impl HasArea for Square
{
  fn area(&self)->f64
  {
     self.side*self.side
  }
}
fn calculate_area(item : T)
{
  println!("Area is : {}",item.area());
}

fn main()
{
  let a = Triangle{base:10.5,height:17.4};
  let b = Square{side : 4.5};
  calculate_area(a);
  calculate_area(b);
}

输出:

Area is : 91.35
Area is : 20.25

在上面的示例中,calculate_area()函数在“ T”上通用。

特质实施规则

实现此特征有两个限制:

  • 如果未在您的范围中定义特征,则无法在任何数据类型上实现。

让我们看一个简单的例子:

 use::std::fs::File;
fn main()
{
  let mut f = File::create("hello.txt");
  let str = "javaTpoint";
  let result = f.write(str);
}

输出:

error : no method named 'write' found.
           let result = f.write(str);
  • 我们要实现的特征必须由我们定义。例如:如果定义HasArea特性,则可以为i32类型实现此特性。但是,由于在板条箱中未同时定义类型和特征,因此我们无法实现Rust为i32类型定义的toString特征。

多重性状界限

  • 使用'+'运算符。

如果要绑定多个特征,可以使用+运算符。

让我们看一个简单的例子:

 use std::fmt::{Debug, Display};
fn compare_prints(t: &T)
 {
 println!("Debug: '{:?}'", t);
 println!("Display: '{}'", t);
}





fn main() {
    let string = "javaTpoint";
    compare_prints(&string);
    }

输出:

Debug: ' "javaTpoint"'
Display: ' javaTpoint'

在上面的示例中,通过使用'+'运算符将Display和Debug特性限制为类型'T'。

  • 使用“ where”子句。
    • 可以使用出现在括号“ {”之前的“ where”子句来编写边界。
    • 'where'子句也可以应用于任意类型。
    • 当使用“ where”子句时,它将使语法比普通语法更具表达力。

我们看看吧:

 fn fun(t:T,v:V)->i32
{
     //block of code;
} 

在上述情况下使用“ where”时:

 fn fun(t:T, v:V)->i32
   where T : Display+ Debug, 
              V : Clone+ Debug
{
        //block of code;
}

在上述情况下,使用“ where”子句的第二种情况使程序更具表现力和可读性。

让我们看一个简单的例子:

 trait Perimeter
{
  fn a(&self)->f64;
}
struct Square
{
  side : f64,
}
impl Perimeter for Square
{
  fn a(&self)->f64
  {
    4.0*self.side
  }
}
struct Rectangle
{
 length : f64,
 breadth : f64,
}
impl Perimeter for Rectangle

{
 fn a(&self)->f64
 {
   2.0*(self.length+self.breadth)
 }
}
fn print_perimeter(s:Square,r:Rectangle)
  where Square : Perimeter,
        Rectangle : Perimeter
        {
          let r1 = s.a();
          let r2 = r.a();
          println!("Perimeter of a square is {}",r1);
          println!("Perimeter of a rectangle is {}",r2);
        }
        fn main()
        {
          let sq = Square{side : 6.2};
          let rect = Rectangle{length : 3.2,breadth:5.6};
          print_perimeter(sq,rect);
        }

输出:

Perimeter of a square is 24.8
Perimeter of a rectangle is 17.6

默认方法

如果已知方法的定义,则可以将默认方法添加到特征定义。

我们看看吧:

 trait Sample
{ 
   fn a(&self);
   fn b(&self)
   {
       println!("Print b");
   } 
} 

在上述情况下,会将默认行为添加到特征定义。我们还可以覆盖默认行为。让我们通过一个例子看一下这种情况:

 trait Sample
{
 fn a(&self);
 fn b(&self)
 {
   println!("Print b");
 } 
} 

struct Example
{
 a:i32,
 b:i32,
}



impl Sample for Example
{
  fn a(&self)
  {
    println!("Value of a is {}",self.a);
  }
  
  fn b(&self)
  {
    println!("Value of b is {}",self.b);
  }
}
fn main()
{
  let r = Example{a:5,b:7};
  r.a();
  r.b();  
}

输出:

Value of a is : 5
Value of b is : 7

在上面的示例中,b()函数的行为在特征中定义。因此,我们可以得出结论,我们可以覆盖特征中定义的方法。

遗产

从另一个特征派生的特征称为继承。有时,必须实施需要实现另一个特征的特征。如果我们想从“ A”特征中导出“ B”特征,则它看起来像:

 trait B : A;

让我们看一个简单的例子:

 trait A
{
  fn f(&self);
}
trait B : A
{
  fn t(&self);
}
struct Example
{
  first : String,
  second : String,
}
impl A for Example
{
  fn f(&self)
  {
 
   print!("{} ",self.first);
  }

 }
 impl B for Example
 {
  fn t(&self)
  {
    print!("{}",self.second);
  }
}
fn main()
{
  let s = Example{first:String::from("javaTpoint"),second:String::from("tutorial")};
  s.f();
  s.t();
}

输出:

javaTpoint tutorial

在上面的示例中,我们的程序正在实现'B'特性。因此,它还需要实现“ A”特性。如果我们的程序未实现'A'特征,则Rust编译器将引发错误。