文章目录
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` trait。
fn 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》