目录
2,PartialEq、Eq、PartialOrd、Ord、Hash
一,结构体、成员方法
给结构体添加方法有2种,一种是直接实现,一种的带trait的实现。
直接实现的方式中,数据和方法是对应的。带trait的实现中,trait是一组可以被共享的行为,只要实现了特征,你就能使用这组行为。
struct SearchData{
}
impl SearchData{
fn f(&self)-> i32{
return 123;
}
fn f2()-> i32{
return 456;
}
}
fn main() {
let x=SearchData{};
assert_eq!(x.f(), 123);
assert_eq!(SearchData::f2(), 456);
}
二,trait(特征)
带trait的实现方式中,数据、接口描述、接口实现要分开。
trait Solution{
fn find(&self)-> i32;
}
struct SearchData{
}
impl Solution for SearchData{
fn find(&self)-> i32{
return 123;
}
}
fn main() {
let x=SearchData{};
println!("{}",x.find());
}
三,同名函数的覆盖、冲突
按照覆盖关系,可以分为三个级别。
1,trait中的默认函数(低级)
trait MyTrait
{
fn my_func()->i32{
-1
}
}
trait中的方法是无法直接调用的。
trait MyTrait
{
fn my_func()->i32{
-1
}
}
struct S{
}
impl MyTrait for S{
}
fn main() {
let x=S{};
assert_eq!(S::my_func(), -1);
println!("end");
}
给结构体S实现了trait,S就自动拥有了默认函数。
2,有无self的函数的多种调用方法
trait MyTrait
{
fn my_func()->i32{
-1
}
fn my_func2(&self)->i32{
-2
}
}
struct S{
}
impl MyTrait for S{
}
fn main() {
let mut x=S{};
assert_eq!(S::my_func(), -1);
assert_eq!(<S as MyTrait>::my_func(), -1);
assert_eq!(x.my_func2(), -2);
assert_eq!((&x).my_func2(), -2);
assert_eq!((&mut x).my_func2(), -2);
assert_eq!(S::my_func2(&x), -2);
assert_eq!(S::my_func2(&mut x), -2);
println!("end");
}
对于无self的函数,可以用类型体名去调用,也可以用类型名转化成Trait名去调用。
对于有self的函数,可以用数据或者数据的各种引用去调用,也可以用类型名或Trait名去调用但是要带上引用形参。
3,引用升级
self形参只允许有& T、&mut T两种。
对于实参,遵循以下几条规则:
(1)带self形参有四种调用方式,分别是数据调用、数据引用调用、类型调用、Trait名调用
PS:这里的引用包括各种各样的引用
PS:这里的类型包括数据结构类型和dyn Trait类型。
还是以这个代码为例:
trait MyTrait
{
fn my_func()->i32{
-1
}
fn my_func2(&self)->i32{
-2
}
fn my_func3(&mut self)->i32{
-3
}
}
struct S{
}
impl MyTrait for S{
}
(2)数据调用,自动把数据转换成引用,前提是满足引用规则。
PS:引用规则就是不可变数据的1种引用和可变数据的2种引用。
let mut x=S{};
assert_eq!(x.my_func2(), -2);
assert_eq!(x.my_func3(), -3);
(3)数据引用调用
&self入参的函数,可以在&x或者&mut x前面随便加引用
&mut self入参的函数,可以在&mut x前面随便加&mut。
PS:满足引用级别指的是级别足够高,&mut的级别高于&的级别
assert_eq!((&x).my_func2(), -2);
assert_eq!((&mut x).my_func2(), -2);
assert_eq!((&&&x).my_func2(), -2);
assert_eq!((&mut x).my_func3(), -3);
//assert_eq!((&mut &x).my_func3(), -3); 引用级别不够
(4)类型调用,规则和数据引用调用完全一致
assert_eq!(S::my_func2(&x), -2);
assert_eq!(S::my_func2(&mut x), -2);
assert_eq!(S::my_func2(&&&x), -2);
assert_eq!(S::my_func3(&mut x), -3);
//assert_eq!(S::my_func3(&mut & x), -3); //引用级别不够
(5)Trait调用,规则和数据引用调用完全一致
assert_eq!(<S as MyTrait>::my_func2(&x), -2);
assert_eq!(<S as MyTrait>::my_func2(&mut x), -2);
assert_eq!(<S as MyTrait>::my_func2(&&&x), -2);
assert_eq!(<S as MyTrait>::my_func3(&mut x), -3);
//assert_eq!(<S as MyTrait>::my_func3(&mut & x), -3); //引用级别不够
(6)完整代码
trait MyTrait
{
fn my_func()->i32{
-1
}
fn my_func2(&self)->i32{
-2
}
fn my_func3(&mut self)->i32{
-3
}
}
struct S{
}
impl MyTrait for S{
}
fn main() {
let mut x=S{};
//数据调用
assert_eq!(x.my_func2(), -2);
assert_eq!(x.my_func3(), -3);
//数据引用调用
assert_eq!((&x).my_func2(), -2);
assert_eq!((&mut x).my_func2(), -2);
assert_eq!((&&&x).my_func2(), -2);
assert_eq!((&mut x).my_func3(), -3);
//assert_eq!((&mut &x).my_func3(), -3); //引用级别不够
//数据类型调用
assert_eq!(S::my_func2(&x), -2);
assert_eq!(S::my_func2(&mut x), -2);
assert_eq!(S::my_func2(&&&x), -2);
assert_eq!(S::my_func3(&mut x), -3);
//assert_eq!(S::my_func3(&mut & x), -3); //引用级别不够
//Trait类型调用
assert_eq!(<S as MyTrait>::my_func2(&x), -2);
assert_eq!(<S as MyTrait>::my_func2(&mut x), -2);
assert_eq!(<S as MyTrait>::my_func2(&&&x), -2);
assert_eq!(<S as MyTrait>::my_func3(&mut x), -3);
//assert_eq!(<S as MyTrait>::my_func3(&mut & x), -3); //引用级别不够
println!("end");
}
4,给结构体实现的trait中的函数(中级)
trait的具体实现会覆盖默认实现:
trait MyTrait
{
fn my_func()->i32{
-1
}
fn my_func2(&self)->i32{
-2
}
fn my_func3(&mut self)->i32{
-3
}
}
struct S{
}
impl MyTrait for S{
fn my_func()->i32{
1
}
fn my_func2(&self)->i32{
2
}
fn my_func3(&mut self)->i32{
3
}
}
fn main() {
let mut x=S{};
assert_eq!(x.my_func2(), 2);
assert_eq!(x.my_func3(), 3);
assert_eq!(S::my_func(), 1);
assert_eq!(S::my_func2(&x), 2);
assert_eq!(S::my_func3(&mut x), 3);
assert_eq!(<S as MyTrait>::my_func(), 1);
assert_eq!(<S as MyTrait>::my_func2(& x), 2);
assert_eq!(<S as MyTrait>::my_func3(&mut x), 3);
println!("end");
}
这是一种彻底的覆盖,对于一个结构体来说,实现了某个trait中的函数,trait中的默认函数就彻底失去了。
从语法来说,两个函数其实是同一个函数,所以用法也是完全一样。
所以,下文的“Trait中的函数”同时指代这2个函数。
5,直接实现的函数(高级)
直接实现的函数有数据调用、数据引用调用、类型调用三种方法。而Trait名调用是Trait中的函数的独有方法。
当同名函数冲突时,这三种方法都会调到优先级更高的直接实现的函数。
PS:这是可以共存的2个不同的函数,都是可以调用到的。
trait MyTrait
{
fn my_func()->i32{
-1
}
fn my_func2(&self)->i32{
-2
}
fn my_func3(&mut self)->i32{
-3
}
}
struct S{
}
impl MyTrait for S{
fn my_func()->i32{
1
}
fn my_func2(&self)->i32{
2
}
}
impl S{
fn my_func()->i32{
11
}
fn my_func2(&self)->i32{
22
}
fn my_func3(&mut self)->i32{
33
}
}
fn main() {
let mut x=S{};
assert_eq!(x.my_func2(), 22);
assert_eq!(x.my_func3(), 33);
assert_eq!((& x).my_func2(), 22);
assert_eq!((&mut x).my_func3(), 33);
assert_eq!(S::my_func(), 11);
assert_eq!(S::my_func2(&x), 22);
assert_eq!(S::my_func3(&mut x), 33);
assert_eq!(<S as MyTrait>::my_func(), 1);
assert_eq!(<S as MyTrait>::my_func2(& x), 2);
assert_eq!(<S as MyTrait>::my_func3(&mut x), -3);
println!("end");
}
6,2个trait之间的同名函数
2个trait之间有同名函数,只能用trait调用。
struct S{
}
trait MyTrait{
fn f1(&self)-> i32;
}
impl MyTrait for S{
fn f1(&self)-> i32{
return 111;
}
}
trait MyTrait2{
fn f1(&self)-> i32;
}
impl MyTrait2 for S{
fn f1(&self)-> i32{
return 222;
}
}
fn main() {
let x=S{};
assert_eq!(MyTrait::f1(&x), 111);
assert_eq!(<S as MyTrait>::f1(&x), 111);
assert_eq!(<S as MyTrait2>::f1(&x), 222);
println!("end");
}
四,trait继承
trait继承是规定,一个类型实现子trait之前,必须先实现父trait。
trait A{
fn funa();
}
trait B:A{
fn funb();
}
trait C:A+B{
fn func();
}
struct S{
}
impl A for S{
fn funa(){
println!("run a");
}
}
impl B for S{
fn funb(){
println!("run b");
}
}
impl C for S{
fn func(){
S::funa();
println!("run c");
}
}
注意,虽然有继承关系,但是可见性却是平级的,父特征中的方法也可以调用子特征中的方法。
例如,自定义排序是这么写的:
#[derive(Eq,PartialEq)]
struct Node{
id:i32,
num:i32
}
impl Ord for Node {
#[inline]
fn cmp(&self, other: &Node) -> Ordering {
if self.num != other.num{
return (*other).num.cmp(&self.num);
}else{
return self.id.cmp(&(*other).id);
}
}
}
impl PartialOrd for Node{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other));
}
}
Ord依赖PartialOrd,但实现上却建议用partial_cmp调用cmp。
我是这么理解的,trait继承表达的是顶层的依赖关系,即设计层面的依赖关系,而trait内的函数调用是底层的依赖关系,即实现层面的依赖关系。
五,trait的孤儿规则
孤儿规则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的。
这条规则保证了代码的安全性,使得无关的代码不会互相影响。
六,trait的关联类型(type成员)
以减法为例
pub trait Sub<Rhs = Self> {
type Output;
fn sub(self, rhs: Rhs) -> Self::Output;
}
用法:
struct S{
x:i32,
y:f32
}
impl Sub for S{
type Output = Self;
fn sub(self,rhs:Self)->Self::Output{
S{x:self.x-rhs.x,y:self.y-rhs.y}
}
}
七,常见trait
0,trait的特殊规则总结
特殊背景规则:
(1)部分类型自动具有Clone、Copy特征
(2)任何类型都不能同时实现Drop和Copy,但是可以同时实现Drop和Clone。
(3)模板类型是默认包含Sized这个特征的,需要 ?Sized 才能说明可以不具有该特征。
(4)只要trait中有一个函数的入参或返回值是Self类型,这个trait就需要继承Sized
特殊潜规则:
(1)永远不要修改ToString中的to_string函数。
(2)永远不要修改Into中的into函数。
1,Clone、Copy
2,PartialEq、Eq、PartialOrd、Ord、Hash
PartialEq、Eq、PartialOrd、Ord、Hash
3,Display、ToString、Debug
ToString中的to_string有默认的泛型实现,里面会调用Display中的fmt,永远不要修改to_string函数。
use std::fmt::Display;
use std::string::ToString;
use std::fmt::Formatter;
use std::fmt::Result;
struct S{
x:i32,
y:f32
}
impl Display for S {
fn fmt(&self, f: &mut Formatter<'_>) -> Result{
write!(f, "x={},y={}", self.x, self.y)
}
}
fn main() {
let x = S{x:3,y:5.5};
assert_eq!(x.to_string(), "x=3,y=5.5");
}
同时也要注意,实现fmt时一定不要调用self.to_string(),否则会造成无限递归。
Debug里面也是fmt函数,一般会比Display的fmt函数的输出内容更详细。
println中的{}会调用Display的fmt,而{:?}和{:#?}会调用Debug的fmt。其中{:#?}是段落格式。
对于原始指针类型,{:?}和{:p}都可以把指针本身的值打出来,不需要特征。
print和println的唯一区别就是,println的末尾多打印一个换行符。
4,Write
use std::io::Write;
和输入输出流有关
5,Sized、From、Into
Sized表示一个类型是否具有确定的大小。
如i32的大小是确定的,而序列 [i32] 的大小是不确定的。
pub trait From<T>: Sized {
fn from(value: T) -> Self;
}
因为返回值类型需要具有Sized,所以只要是有Self类型的返回值函数的Trait,都需要继承Sized
pub trait Into<T>: Sized {
fn into(self) -> T;
}
因为入参类型需要具有Sized,所以只要是有有Self类型的入参的函数的Trait,都需要继承Sized
PS:只针对单纯的Self类型,各种引用类型不算,因为引用本身肯定是Sized的。
Into的into有默认的泛型实现:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T
where
U: From<T>,
{
#[inline]
fn into(self) -> U {
U::from(self)
}
}
所以永远不要修改into函数。
6,Default
default函数类似于new函数,用于申请一个对象:
pub trait Default: Sized {
#[stable(feature = "rust1", since = "1.0.0")]
fn default() -> Self;
}
需要显式地调用default函数才能获取具有默认值的对象,而new函数用于获取任意值的对象。
7,Drop
Drop用于定义一个类型的析构函数:
pub trait Drop {
fn drop(&mut self);
}
这是系统自动调用的函数,用户可以实现这个函数,但不能调用它,如果写了调用代码,则会编译报错:explicit use of destructor method
但是,rust提供一个泛型的drop函数:
pub fn drop<T>(_x: T) {}
函数实现是空的,作用是释放一个变量的所有权,函数调用完后立刻析构。
任何类型都不能同时实现Drop和Copy,但是可以同时实现Drop和Clone。
系统自动调用Drop中的drop的规则:
(1)一个数据的所有权丢失了,不被任何变量持有,则立刻调用Drop中的drop
(2)同一个函数内的栈变量,按照后定义的先释放,依次调用Drop中的drop
示例:
struct S{
x:i32
}
impl Drop for S{
fn drop(&mut self){
print!("x={} ", self.x);
}
}
fn main() {
let mut a=S{x:1};
let b=S{x:2};
let c=S{x:3};
drop(b);
a.x=1;
println!("end");
}
输出:
x=2 end
x=3 x=1
无论有没有最后的a.x=1;这一句,都是这个输出。
(3)如果一个数据的所有权发生了转移,则按照最后持有所有权的变量的定义位置进行排序,依次调用Drop中的drop
示例:
struct S{
x:i32
}
impl Drop for S{
fn drop(&mut self){
print!("x={} ", self.x);
}
}
fn main() {
let a=S{x:1};
let b=S{x:2};
let c=S{x:3};
let d=a;
drop(b);
println!("end");
}
输出:
x=2 end
x=1 x=3
(4)嵌套数据结构,先调用外层的drop,再逐层往里调用drop,同一级成成员,按照定义的顺序依次析构。
示例:
struct SS(&'static str);
impl Drop for SS{
fn drop(&mut self){
println!("Dropping {}",self.0);
}
}
struct S{
a:SS,
b:SS
}
impl Drop for S{
fn drop(&mut self){
println!("Dropping S");
}
}
fn main() {
let x=S{
a:SS("a"),
b:SS("b")
};
}
输出:
Dropping S
Dropping a
Dropping b
(5)结构体的成员变量,按照声明顺序析构。枚举、元组等都类似。
(6)闭包通过移动(move)语义捕获的变量的销毁顺序未明确指定,这是一个未定义行为。
8,Borrow、BorrowMut
参考引用
9,Deref、DerefMut
参考隐式转换