目录
1,泛型函数
2,特征约束
(1)特征约束
(2)多重约束
(3)where
(4)子特征的约束推导出父特征
(5)模板类型的默认特征约束
(6)trait类型的入参
3,泛型数据结构
(1)泛型结构体
(2)泛型结构体实现trait的偏特化实现
(3)泛型结构体实现trait的泛型实现、带type的特征约束
(4)泛型枚举
4,常数泛型参数
5,泛型trait
6,trait内的泛型函数
7,trait类型的返回值
8,trait对象
下面是一个手动实现vec翻转的例子:
fn vector_reverse (v:&Vec)->Vec{
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
第二个v:&Vec
第三个->Vec
也可以使用多个模板参数:
fn f (v:&Vec,s:&Vec)->i32
{
return 0;
}
vector_reverse的例子中,要求模板参数T具有Clone特征,这就是一个特征约束。
泛型函数要保证自身是能编译的,而不取决于调用代码。所以,泛型函数内部对T的约束条件,都通过指明T所包含的trait的方式进行说明。
如果T需要多个trait,采用加号把trait连接起来。
如果特征约束比较多,为了不影响阅读,可以把约束提到函数头的末尾:
fn f (v:&Vec,s:&Vec)->i32
where T:Clone, S:Copy
{
return 0;
}
fn f2(arr:Vec)->T{
if(arr[0]==arr[1]){
return arr[1].clone();
}
return arr[0].clone();
}
Ord特征中并没有eq函数,但是Ord特征间接继承了PartialOrd,所以有Ord特征的类型肯定是可以使用==的。
对于绝大部分trait,模板类型都是默认不包含该trait的,需要特征约束才能说明该类型具有该trait。
而Sized是个反例,模板类型是默认包含Sized这个特征的,需要 ?Sized 才能说明可以不具有该特征。
pub trait Borrow {
......
}
fn exec(x:impl Display){
print!("{}",x);
}
fn main() {
exec(6);
}
这是一个语法糖,等价于:
fn exec(x:T){
print!("{}",x);
}
数据结构要想好用,都得是泛型的。
无论是c++ STL还是rust std,里面所有的数据结构都是泛型的,c++和rust的结构体也类似,可以是泛型的也可以是非泛型的。
例如 Vec
pub struct Vec {
buf: RawVec,
len: usize,
}
trait VecEx {
fn get_zeros_num(&self) -> i32;
}
impl VecEx for Vec {
fn get_zeros_num(&self) -> i32{
let mut ans = 0;
for i in 0..self.len() {
if (self[i] == 0) {
ans+=1;
}
}
return ans;
}
}
use std::ops::Sub;
trait VecEx {
fn get_zeros_num(&self) -> i32;
}
impl+PartialEq> VecEx for Vec {
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;
}
}
例如option、result
pub enum Option {
None,
Some(T),
}
pub enum Result {
Ok(T),
Err(E),
}
和c++类型,常数也可以作为泛型参数
fn f(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类型
给结构体实现泛型trait,会遇到一些比较复杂的情况
(1)同名且返回值相同
struct S
{
x:T,
y:T2
}
trait MyTrait{
fn f(&self)->i32;
}
impl MyTrait for T{
fn f(&self)->i32{
-1
}
}
impl MyTrait for S{
fn f(&self)->i32{
1
}
}
fn main() {
let x=S{x:1, y:1};
assert_eq!( as MyTrait>::f(&x), 1);
assert_eq!( as MyTrait>>::f(&x), -1);
println!("end");
}
这样,实际上给S实现了2份MyTrait,必须用trait名调用函数。
(2)同名但返回值不同
参考Rc中的borrow函数
struct S{
x:i32,
y:f32
}
trait Tr {
fn f(x:& T){}
}
impl Tr for S{
fn f(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是靠入参自动推导出来的。
fn f()->impl Display{
4546
}
fn exec(x:T){
print!("{}",x);
}
fn main() {
let x = f();
exec(x);
}
f可以返回任意具有Display特征的类型的数据。
用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);
}