Rust 专题
您的位置:rust > Rust专题 > Rust Trait
Rust Trait
作者:--    发布时间:2019-11-20

rust trait 是rust语言的一个特性(性状),它描述了它可以提供的每种类型的功能。
性状类似于其他语言中定义的接口的特征。
性状是一种对方法签名进行分组以定义一组行为的方法。
使用trait关键字定义性状。

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<t>( item : t)  
   println!(?area of a triangle is {}?, item.area());

在上面的例子中,rust编译器抛出“没有找到类型为t的方法的错误”。 如果将性状绑定到泛型t,则可以解决以下错误:

fn calculate_area<t : hasarea> (item : t)  
{  
    println!("area of a triangle is {} ",item.area());  
}

在上面的例子中,<t:hasarea>表示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<t : hasarea>(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 = "h3";  
  let result = f.write(str);  
}

执行上面示例代码,得到以下结果 -

error : no method named 'write' found.
           let result = f.write(str);

在上面的例子中,rust编译器抛出一个错误,即"no method named 'write' found"use::std::fs::file;, 命名空间不包含write()方法。 因此,需要使用write trait来删除编译错误。

  • 正在实现的性状必须定义。 例如:如果定义hasarea性状,那么要为i32类型实现这个性状。 但是,无法为类型i32实现rust定义的tostring性状,因为类型和性状没有在包中定义。

多个性状界限

使用'+'运算符。

如果想绑定多个性状,可使用+运算符。

下面来看一个简单的例子:

use std::fmt::{debug, display};  
fn compare_prints<t: debug + display>(t: &t)  
{  
    println!("debug: '{:?}'", t);  
    println!("display: '{}'", t);  
}  

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

执行上面示例代码,输出结果如下 -

debug: ' "h3"'
display: ' h3'

在上面的示例中,displaydebug特性通过使用+运算符限制为类型t

使用where子句。

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

如下代码 -

fn fun<t: display+debug, v: clone+debug>(t:t,v:v)->i32  
//block of code;

在上述情况下使用where时:

fn fun<t, v>(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<square,rectangle>(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("h3"),second:string::from("tutorial")};  
  s.f();  
  s.t();  
}

执行上面示例代码,得到以下结果 -

h3 tutorial

在上面的例子中,程序实现’b’性状。 因此,它还需要实现’a’性状。 如果程序没有实现’a’性状,则rust编译器会抛出错误。


网站声明:
本站部分内容来自网络,如您发现本站内容
侵害到您的利益,请联系本站管理员处理。
联系站长
373515719@qq.com
关于本站:
编程参考手册