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());
}
在上面的例子中,Rust编译器知道任何实现<T:HasArea>
T
HasArea
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 = "Yiibai";
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
toString
使用'+'
如果想系结多个性状,可使用+
下面来看一个简单的例子:
use std::fmt::{Debug, Display};
fn compare_prints<T: Debug + Display>(t: &T)
{
println!("Debug: '{:?}'", t);
println!("Display: '{}'", t);
}
fn main() {
let string = "Yiibai";
compare_prints(&string);
}
执行上面范例程式码,输出结果如下 -
Debug: ' "Yiibai"'
Display: ' Yiibai'
在上面的范例中,Display
Debug
+
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("Yiibai"),second:String::from("tutorial")};
s.f();
s.t();
}
执行上面范例程式码,得到以下结果 -
Yiibai tutorial
在上面的例子中,程式实现'B'性状。因此,它还需要实现'A'性状。如果程式没有实现'A'性状,则Rust编译器会丢掷错误。