Rust trait特性与约束


Rust 依赖库管理工具–cargo。如果你曾经手动管理过库依赖,那么你就知道这会带来的极大的痛苦。在Rust 的生态链下,有cargo 可以管理项目的各种依赖关系。

//  二进制可执行文件
cargo new foo

// 或者库
cargo new --lib foo

一、trait

如下定义了一个泛型 trait ,它把 Drop trait 作为泛型方法实现了,可以 drop (丢弃)调用者本身和一个输入参数。

struct Empty;  // 定义默认不可复制的类型
struct Null;


trait DoubleDrop<T> {
    fn double_drop(self,_:T);
}

//此方法获得两个传入参数的所有权,并释放它们。
impl<T,U>DoubleDrop<T> for U{
    fn double_drop(self, _:T) {}
}


fn main() {
    let empty = Empty;
    let null = Null;
    empty.double_drop(null);
    
    println!("Hello, world!");
}

二、约束

在使用泛型时,类型参数常常必须使用 trait 作为约束(bound)来明确规定 类型应实现哪些功能。例如下面的例子用到了 Display trait 来打印,所以它用 Display 来约束 T ,也就是说 T 必须实现 Display 。

// 定义接受一个类型为泛型 `T` 的参数的函数,其中 `T` 必须实现 `Display` traitfn printer<T: Display>(t: T) {
	println!("{}", t);
}

约束的另一个作用是泛型的实例可以访问作为约束的 trait 的方法。例如:

use std::fmt::Debug;

#[derive(Debug)]
struct Rectangle{length:f64, height:f64}    // 定义矩阵
struct Triangle{length:f64, height:f64}     // 三角形

trait HasArea {
    fn area(&self) ->f64;
}

impl HasArea for Rectangle{
    fn area(&self) ->f64 { self.length * self.height}
}

impl HasArea for Triangle{
    fn area(&self) ->f64 { 0.5 * self.length * self.height}
}

//泛型`T`必须实现`Debug`。只要满足这点,无论什么类型,都可以让下面函数正常工作
fn print_debug<T:Debug>(t:&T){
    println!("{:?}",t);
}

// `T`必须实现`HasArea`。任意符合该约束的泛型的实例,都可访问`HasArea`的`area`函数
fn area<T:HasArea>(t:&T)->f64 {t.area()}

fn main() {
    let rectangle = Rectangle{length:10.0,height:20.0};
    let _triangle = Triangle{length:20.0,height:10.0};

    print_debug(&rectangle);
    println!("Rectangle Area is : {}",area(&rectangle));
    println!("Triangle  Area is : {}",area(&_triangle));
}

// Rectangle { length: 10.0, height: 20.0 }
// Rectangle Area is : 200
// Triangle  Area is : 100

是不是很神奇,area()会根据传入的对象调用为该对象实现的特性函数。
当然了,约束可以为空。

三、多重约束

多重约束(multiple bounds)可以用 + 连接。和平常一样,类型之间使用 , 隔开。

为了方便,约束也可以使用 where 分句来表达,它放在 { 的前面,而不需写在类型第一次出现之前。另外 where 从句可以用于任意类型的限定,而不局限于类型参数本身。

impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}

// 使用 `where` 从句来表达约束
impl <A, D> MyTrait<A, D> for YourType where
	A: TraitB + TraitC,
	D: TraitE + TraitF {}
use std::fmt::Debug;

trait PrintInOption{
    fn print_in_option(self);
}


impl<T> PrintInOption for T where
    Option<T>:Debug{
    fn print_in_option(self){
        println!{"{:?}",Some(self)};
    }
}

fn main(){
    let vec1 = vec![1,2,3];
    vec1.print_in_option();
}

四、关联项

“关联项”(associated item)指与多种类型的项有关的一组规则。它是 trait 泛型的扩展,允许在 trait 内部定义新的项。

一个这样的项就叫做一个关联类型。当 trait 对于实现了它的容器类型是泛型的,关联项就提供了简单的使用方法。

// 不使用关联类型
fn difference<A, B, C>(container: &C) -> i32 where
C: Contains<A, B> { ... }

// 使用关联类型
fn difference<C: Contains>(container: &C) -> i32 { ... }

示例:

struct Container(i32,i32);


// 整个特性中可以看出,传入参数都为整型引用(可复制类型)
trait Contains{
    type A;
    type B;

    fn contains(&self,_:&Self::A,_:&Self::B)->bool;
    fn first(&self)->i32;
    fn last(&self)->i32;
}

impl Contains for Container {
    type A = i32;
    type B = i32;

    fn contains(&self,number_1:&i32,number_2:&i32) -> bool {
        (&self.0 == number_1) &&(&self.1 == number_2)
    }

    fn first(&self) ->i32 {self.0}
    fn last(&self) ->i32 {self.1}
}

// 使用关联类型
fn difference<C:Contains>(container:&C) ->i32{
    (container.last() - container.first()) as i32
}


fn main() {
    let number_1 = 3;
    let number_2 =10;
    let container = Container(number_1,number_2);
    println!("Does container contain {} and {} : {}",
             &number_1,&number_2,
             container.contains(&number_1,&number_2));

    println!("First number :{}",container.first());
    println!("Second number :{}",container.last());
    println!("Two number difference is {}",difference(&container))
}

// Does container contain 3 and 10 : true
// First number :3
// Second number :10
// Two number difference is 7

五、虚类型参数

虚类型(phantom type)参数是一种在运行时不出现,而在(且仅在)编译时进行静态检查的类型参数。

可以用额外的泛型类型参数指定数据类型,这类型可以充当标记,也可以供编译时类型检查使用。这些额外的参数没有存储值,也没有运行时行为。

使用std::marker::PhantomData作为虚类型参数的类型,创建包含不同数据类型的元组。

use std::marker::PhantomData;
//这个虚元组结构体对`A`是泛型的,并且带有隐藏参数`B`。
#[derive(PartialEq)] //允许这种类型进行相等测试(equalitytest)。
struct PhantomTuple<A, B>(A,PhantomData<B>);

//这个虚类型结构体对`A`是泛型的,并且带有隐藏参数`B`。
#[derive(PartialEq)]
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }
//注意:对于泛型`A`会分配存储空间,但`B`不会。因此,`B`不能参与运算。

测试实例:单位说明:

use std::ops::Add;
use std::marker::PhantomData;


//创建空枚举类型表示单位
#[derive(Debug, Clone, Copy)]
enum Inch{}
#[derive(Debug, Clone, Copy)]
enum Mm{}
#[derive(Debug, Clone, Copy)]
struct Length<Unit>(f64,PhantomData<Unit>);

impl <Unit> Add for Length<Unit>{
    type Output = Length<Unit>;

    fn add(self, rhs: Length<Unit>) -> Length<Unit>{
        Length(self.0 + rhs.0,PhantomData)
    }
}


fn main() {
    let foot_1:Length<Inch> = Length(12.0,PhantomData);
    let foot_2:Length<Mm> = Length(1000.0,PhantomData);

    // 必须为空枚举体与虚类型实现clone,否则如下计算会移动所有权无法编译通过,
    let two_foot1 = foot_1 + foot_1;
    let two_foot2 = foot_2 + foot_2;

    println!("foot_1 + foot_1 = {:?} in",two_foot1.0);
    println!("foot_2 + foot_2 = {:?} mm",two_foot2.0);

    // 编译期错误:类型不匹配。
    // let foot1_foot2 = one_foot + one_meter;
}

// foot_1 + foot_1 = 24.0 in
// foot_2 + foot_2 = 2000.0 mm

如上示例可见,通过虚类型对对象进行约束,只允许相同单位的数据进行计算。

以上代码来自于《通过例子学rust》

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页