一,泛型
1,泛型函数
下面是一个手动实现vec翻转的例子:
fn vector_reverse<T:Clone> (v:&Vec<T>)->Vec<T>{let mut ans = Vec::new();let mut i = v.len();loop {if i==0{break;}i-=1;ans.push(v[i].clone());}return ans;
}
这里一共有3处类型参数T
第一个vector_reverse<T:Clone>表示这个泛型函数的的参数就是T
第二个v:&Vec<T>表示入参是T类型的vec
第三个->Vec<T>表示返回值是T类型的vec
也可以使用多个模板参数:
fn f<T:Clone, S:Copy> (v:&Vec<T>,s:&Vec<S>)->i32
{return 0;
}
2,特征约束
(1)特征约束
vector_reverse的例子中,要求模板参数T具有Clone特征,这就是一个特征约束。
泛型函数要保证自身是能编译的,而不取决于调用代码。所以,泛型函数内部对T的约束条件,都通过指明T所包含的trait的方式进行说明。
(2)多重约束
如果T需要多个trait,采用加号把trait连接起来。
(3)where
如果特征约束比较多,为了不影响阅读,可以把约束提到函数头的末尾:
fn f<T, S> (v:&Vec<T>,s:&Vec<S>)->i32where T:Clone, S:Copy
{return 0;
}
(4)子特征的约束推导出父特征
fn f2<T:Ord+Clone>(arr:Vec<T>)->T{if(arr[0]==arr[1]){return arr[1].clone();}return arr[0].clone();
}
Ord特征中并没有eq函数,但是Ord特征间接继承了PartialOrd,所以有Ord特征的类型肯定是可以使用==的。
(5)模板类型的默认特征约束
对于绝大部分trait,模板类型都是默认不包含该trait的,需要特征约束才能说明该类型具有该trait。
而Sized是个反例,模板类型是默认包含Sized这个特征的,需要 ?Sized 才能说明可以不具有该特征。
pub trait Borrow<Borrowed: ?Sized> {
......
}
(6)trait类型的入参
fn exec(x:impl Display){print!("{}",x);
}fn main() {exec(6);
}
这是一个语法糖,等价于:
fn exec<T:Display>(x:T){print!("{}",x);
}
3,泛型数据结构
数据结构要想好用,都得是泛型的。
无论是c++ STL还是rust std,里面所有的数据结构都是泛型的,c++和rust的结构体也类似,可以是泛型的也可以是非泛型的。
(1)泛型结构体
例如 Vec
pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {buf: RawVec<T, A>,len: usize,
}
(2)泛型结构体实现trait的偏特化实现
trait VecEx {fn get_zeros_num(&self) -> i32;
}
impl VecEx for Vec<i32> {fn get_zeros_num(&self) -> i32{let mut ans = 0;for i in 0..self.len() {if (self[i] == 0) {ans+=1;}}return ans;}
}
(3)泛型结构体实现trait的泛型实现、带type的特征约束
use std::ops::Sub;
trait VecEx {fn get_zeros_num(&self) -> i32;
}
impl<T:Clone+Sub<Output=T>+PartialEq> VecEx for Vec<T> {fn get_zeros_num(&self) -> i32{let mut ans = 0;for i in 0..self.len() {if (self[i] == self[0].clone() - self[0].clone()) {ans+=1;}}return ans;}
}
(4)泛型枚举
例如option、result
pub enum Option<T> {None,Some(T),
}pub enum Result<T, E> {Ok(T),Err(E),
}
4,常数泛型参数
和c++类型,常数也可以作为泛型参数
fn f<T:Clone,const N:usize>(arr:[T;N])->T{return arr[0].clone();
}fn main() {let x = [1,2,3];let y = [1.5,2.5];assert_eq!(f(x),1);assert_eq!(f(y),1.5);
}
作为泛型参数的常数类型,可以是所有整数类型、bool类型、char类型
5,泛型trait
给结构体实现泛型trait,会遇到一些比较复杂的情况
(1)同名且返回值相同
struct S<T, T2>
{x:T,y:T2
}
trait MyTrait<T>{fn f(&self)->i32;
}impl<T> MyTrait<T> for T{fn f(&self)->i32{-1}
}impl<T,T2> MyTrait<T> for S<T,T2>{fn f(&self)->i32{1}
}fn main() {let x=S{x:1, y:1};assert_eq!(<S<i32,i32> as MyTrait<i32>>::f(&x), 1);assert_eq!(<S<i32,i32> as MyTrait<S<i32,i32>>>::f(&x), -1);println!("end");
}
这样,实际上给S实现了2份MyTrait,必须用trait名调用函数。
(2)同名但返回值不同
参考Rc中的borrow函数
6,trait内的泛型函数
struct S{x:i32,y:f32
}trait Tr {fn f<T:Display>(x:& T){}
}impl Tr for S{fn f<T:Display>(x:& T){println!("data = {}", x);}
}fn main() {let mut s=S{x:5, y:7.7};S::f(&s.x);S::f(&s.y);
}
这个例子中,类型T是靠入参自动推导出来的。
7,trait类型的返回值
fn f()->impl Display{4546
}
fn exec<T:Display>(x:T){print!("{}",x);
}fn main() {let x = f();exec(x);
}
f可以返回任意具有Display特征的类型的数据。
8,trait对象
用dyn关键字,可以把一个trait当做一个数据类型。
前提条件:该trait的所有函数都有self参数。
struct S{x:i32
}
struct S2{y:f32
}trait Ft{fn f(&self){}fn f2(&self){}
}
impl Ft for S{fn f(&self){print!("{}",self.x);}
}
impl Ft for S2{fn f(&self){print!("{}",self.y);}
}fn exec(x: &dyn Ft) {x.f();
}fn main() {let s1 = S{x:5};let s2=S2{y:2.3};exec(&s1);exec(&s2);
}
二,隐式转换
0,背景知识
解引用操作符* 和 引用操作符&或者&mut 是相反的。
所以,只要编译器遇到*&,或者*&mut,会直接抵消掉。
只要在类型T1中定义了1个函数f,把T1数据转换成&T2或者&mut T2类型,那么就有如下的代码:
let x:T1=T1{};
let y=*x.f();
如果这个函数是deref或者deref_mut,那么就直接写成*x就行了。
可以说这是自定义解引用,也可以理解成一种隐式转换。
rust中只能自定义解引用,不能自定义引用。
1,Deref
rust语言对类型检查比较严格,不同类型之间是没有隐式转换的,除非实现了Deref这个trait
只要实现了Deref,就自动拥有了对应的隐式转换能力。
(1)定义
pub trait Deref {type Target: ?Sized;fn deref(&self) -> &Self::Target;
}
(2)示例:Vec转切片
impl<T, A: Allocator> ops::Deref for Vec<T, A> {type Target = [T];#[inline]fn deref(&self) -> &[T] {unsafe { slice::from_raw_parts(self.as_ptr(), self.len) }}
}
使用示例:
let c:Vec<u8>=Vec::new();let d:&[u8]=&c;
这里,&c本来是&Vec的类型,但是由于隐式转换,所以可以赋值给&[u8]类型的变量。
(3)自定义例子
use std::ops;
use ops::Deref;#[derive(Clone,Copy)]
struct S1{
}
struct S2{
}impl ops::Deref for S2{type Target = S1;fn deref(&self)->&S1{&S1{}}
}fn show(x:S1){println!("show");
}fn main() {let x:S1=S1{};show(x);let x2=S2{};show(*x2.deref());show(*x2); println!("end");
}
这里展示了deref原始语义的例子,即*x2.deref()可以省略成*x2,这个例子中,deref就是自定义解引用,解引用完之后就变成了S1类型。
(4)隐式转换
实际上没有*操作符,deref也能发挥隐式转换的作用,但是仅限在函数参数传递过程中。
待更新
参考:
5分钟速读之Rust权威指南(二十五)Deref - 知乎
Rust-解引用-CSDN博客