rust std、数据结构、算法

目录

一,std常用数据结构

1,std::option

2,std::result

3,字符串

二,std容器

三,自定义数据结构

1,单链表(力扣版)

2,二叉树(力扣版)

四,std::cmp

1,min函数、max函数

2,Reverse

五,std算法、自实现算法

1,排序

(0)sort 和 sort_unstable

(1)vector排序

(2)自定义排序

2,二分

(1)vector二分

(2)自定义二分

3,哈希算法

4,BFS

5,并查集

六,通用函数

1,new函数

2,from

七,二元关系特征

0,继承、一致性大前提

1,std::cmp::PartialEq、lhs、rhs

2,std::cmp::Eq

3,std::cmp::PartialOrd

4,PartialOrd的二元关系推导

5,std::cmp::Ord

6,默认序关系

7,自定义序关系的场景


rust std 文档

一,std常用数据结构

use std::option;

1,std::option

(1)定义

pub enum Option<T> {
    None,
    Some(T),
}

这是一个枚举值,要么等于整型的None,要么等于泛型的只含一个元素的元组Some

(2)is_some、is_none

	let x:Option<u32>=Some(2);
	assert_eq!(x.is_some(),true);
	assert_eq!(x.is_none(),false);
	let x:Option<u32>=None;
	assert_eq!(x.is_some(),false);
	assert_eq!(x.is_none(),true);

(3)is_some_and

	let x:Option<u32>=Some(2);
	assert_eq!(x.is_some_and(|x| x>1),true);

(4)unwrap

当我们确定一个Option值是Some值时,可以直接调用unwrap把Some里面的值取出来。

    let p = Some(5);
    assert_eq!(p.unwrap(),5);

2,std::result

use std::result;

(1)定义

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

这也是个枚举值,要么是等于泛型的元组OK,要么是等于泛型的元组Err

2个模板类型可以一样也可以不一样。

(2)is_ok、is_err

	let x:Result<i32,i32>=Ok(1);
	assert_eq!(x.is_ok(),true);
	assert_eq!(x.is_err(),false);
	let x:Result<i32,&str>=Err("456");
	assert_eq!(x.is_ok(),false);
	assert_eq!(x.is_err(),true);

str类型要带取址符。

(3)is_ok_and、is_err_and

	let x:Result<i32,i32>=Ok(1);
    assert_eq!(x.is_ok_and(|x| x==1),true);
	assert_eq!(x.is_err(),false);
	let x:Result<i32,&str>=Err("456");
	assert_eq!(x.is_ok(),false);
	assert_eq!(x.is_err_and(|x| x=="456"),true);

(4)ok、err

用ok、err可以直接取出2个成员值,有一个成员会是Some值,另一个会是None值。

	let x:Result<i32,i32>=Ok(1);
    assert_eq!(x.ok(),Some(1));
	assert_eq!(x.err(),None);
	let x:Result<i32,&str>=Err("456");
	assert_eq!(x.ok(),None);
	assert_eq!(x.err(),Some("456"));

(5)unwrap

    let x:Result<i32,i32>=Ok(1);
    assert_eq!(x.unwrap(),1);
    assert_eq!(x.ok().unwrap(),1);

(6)map

源码:

    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Result<U, E> {
        match self {
            Ok(t) => Ok(op(t)),
            Err(e) => Err(e),
        }
    }

示例:

fn main() {
    let x:Result<i32,i32>=Ok(1);
    let x=x.map(|x| x+1);
    assert_eq!(x,Ok(2));
    let x:Result<i32,i32>=Err(1);
    let x=x.map(|x| x+1);
    assert_eq!(x,Err(1));
    print!("end ");
}

因为Result的map方法是只对Ok生效的

3,字符串

(0)字符串编码

参考这里

(1)String的实现

use std::string::String;

pub struct String {
    vec: Vec<u8>,
}

(2)String和Vec<char>互转

StringVec<char>直接互转,该映射是双射,不会失败

    let s=String::from("1234");
    let v:Vec<char>=s.chars().collect();
    assert_eq!(v.len(),4);
    let s:String = v.into_iter().collect();
    assert_eq!(s,String::from("1234"));

整数转字符串:

fn int_to_string(x:i32)->String{
    let mut ans = String::new();
    let mut v=Vec::new();
    let mut x = x;
    let mut c='0';
    while x>0 {
        v.insert(0,(c as u8 + (x%10) as u8)as char);
        x/=10;
    }
    return v.into_iter().collect();
}

字符串转整数

fn string_to_int(v:& Vec<char>)->i32{
    let mut id :usize=0;
    let mut flag:i32 = 1;
    if v[id] == '-' {
        id+=1;
        flag = -1;
    }
    let mut s:i32 = 0;
    while id < v.len() {
        s = s * 10 + v[id].to_digit(10).unwrap() as i32;
        id+=1;
    }
    return s*flag;
}

(3)StringVec<u8>互转

StringVec<u8>互转, StringVec<u8> 是单射但不是满射,所以from_utf8返回值是Result

    let s=String::from("哈喽,世界");
    let v:Vec<u8> = s.into_bytes();
    let s = String::from_utf8(v).unwrap();
    assert_eq!(s,"哈喽,世界");

(4)&str的实现

str是内置类型,表示[u8],即u8类型的切片。

由于切片类型没有固定的size,不能用于栈中,所以我们一般用的都是&str

字符串字面量的引用也是&str

fn get_max_char(s:&str) -> char{
    let mut ans:char = s.chars().next().unwrap();
    for x in s.chars(){
        if ans < x {
            ans = x;
        }
    }
    return ans;
}

fn main() {
    let s:&str = "hello world";
    assert_eq!(get_max_char(s), 'w');
    println!("end");
}

(5)&str和String互转

完整切片:String&str是双射

    let a:&str = "hello world";
    let b:String = String::from(a);
    let c:&str = &b;
    let d:&str = &b[..];

部分引用切片:可能panic

    let s: String = String::from("猜猜我是谁");
    let b: &str = &s[0..6];
    assert_eq!(b,"猜猜");
    let c: &str = &s[1..6]; // thread panicked

也就是说,strString一样,虽然底层实现是u8组成的,但和u8序列并不双射,而是和字符串双射。

(6)&str和Vec<char>互转

    let s:&str = "hello world";
    let v:Vec<char>=s.chars().collect();
    let s2:String = v.into_iter().collect();
    let s3:&str=&s2;

(7)&str和Vec<u8>互转

    let s:&str = "hello world";
    let v:Vec<u8> = s.as_bytes().to_vec();
    let s2 = String::from_utf8(v).unwrap();
    let s3:&str=&s2;

(8)完整互转有向图

左边2个构成u8域,右边3个构成char域,域内双射,从char域到u8是单射非满射。

二,std容器

参考rust容器、迭代器

三,自定义数据结构

1,单链表(力扣版)

链表定义:

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
  pub val: i32,
  pub next: Option<Box<ListNode>>
}

impl ListNode {
  #[inline]
  fn new(val: i32) -> Self {
    ListNode {
      next: None,
      val
    }
  }
}

单链表和vector的互转:

fn trans_link_list_to_vector(head: Option<Box<ListNode>>)->Vec<i32>{
    let mut v=Vec::new();
    if head.is_none(){
        return v;
    }
    let mut p = & mut head.unwrap();
    v.push(p.val);
    loop{
        if let Some(ref mut x) = p.next {
            v.push(x.val);
            p=x;
        }else{
            break;
        }
    }
    return v;
}
fn trans_vector_to_link_list(v:Vec<i32>)-> Option<Box<ListNode>> {
    let mut ans = Box::new(ListNode::new(0));
    let mut p =&mut ans;
    for i in 0..v.len() {
        p.next = Some(Box::new(ListNode::new(v[i])));
        if let Some(ref mut x) = p.next{
            p=x;
        }
    }
    return ans.next;
}

2,二叉树(力扣版)

二叉树定义:

use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug, PartialEq, Eq)]
pub struct TreeNode {
  pub val: i32,
  pub left: Option<Rc<RefCell<TreeNode>>>,
  pub right: Option<Rc<RefCell<TreeNode>>>,
}
impl TreeNode {
  #[inline]
  pub fn new(val: i32) -> Self {
    TreeNode {
      val,
      left: None,
      right: None
    }
  }
}

序列化和反序列化:


fn serialize(root: Option<Rc<RefCell<TreeNode>>>, error: i32)->Vec<i32> {
    let mut q=VecDeque::new();
    q.push_back(root.clone());
    let mut ans = Vec::new();
    while q.len() > 0{
        let p = q.pop_front().unwrap();
        if p.is_none() {
            ans.push(error);
            continue;
        }
        let r = p.clone().unwrap();
        ans.push(r.borrow_mut().val);
        q.push_back(r.borrow_mut().left.clone());
        q.push_back(r.borrow_mut().right.clone());
    }
    return ans;
}
fn deserialize(v:Vec<i32>, error:i32) ->Option<Rc<RefCell<TreeNode>>>{
    if v[0]==error {
        return None;
    }
    let mut id:usize = 0;
    let val = v[id];
    id+=1;
    let root = Some(Rc::new(RefCell::new(TreeNode::new(val))));
    let mut q=VecDeque::new();
    q.push_back(root.clone());
    while q.len() > 0 {
        let p = q.pop_front().unwrap();
        let r = p.clone().unwrap();
        let val = v[id];
        id+=1;
        if val != error {
            r.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(val))));
            q.push_back(r.borrow_mut().left.clone());
        }
        let val = v[id];
        id+=1;
        if val != error {
            r.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(val))));
            q.push_back(r.borrow_mut().right.clone());
        }
    }
    root
}

四,std::cmp

1,min函数、max函数

std::cmp::min

std::cmp::max

2,Reverse

这是一个泛型的元组结构体,实现了泛型的逆序:

pub struct Reverse<T>(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T);

#[stable(feature = "reverse_cmp_key", since = "1.19.0")]
impl<T: PartialOrd> PartialOrd for Reverse<T> {
    #[inline]
    fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> {
        other.0.partial_cmp(&self.0)
    }

    #[inline]
    fn lt(&self, other: &Self) -> bool {
        other.0 < self.0
    }
    #[inline]
    fn le(&self, other: &Self) -> bool {
        other.0 <= self.0
    }
    #[inline]
    fn gt(&self, other: &Self) -> bool {
        other.0 > self.0
    }
    #[inline]
    fn ge(&self, other: &Self) -> bool {
        other.0 >= self.0
    }
}

#[stable(feature = "reverse_cmp_key", since = "1.19.0")]
impl<T: Ord> Ord for Reverse<T> {
    #[inline]
    fn cmp(&self, other: &Reverse<T>) -> Ordering {
        other.0.cmp(&self.0)
    }
}

五,std算法、自实现算法

1,排序

(0)sort 和 sort_unstable

sort是Timsort,sort_unstable是pdqsort,参考常见排序算法

(1)vector排序

fn main() {
	let mut nums=vec![1,2,4,3];
	nums.sort();
	println!("{}",nums[3]);
}

vector的默认排序,从小到大排序,输出4

(2)自定义排序

给自定义结构体添加自定义排序规则,需要实现Ord特征

结构体实现:

#[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)); 
    }
}

Ordering是枚举值,分别表示小于等于大于。

形如self.cmp(other)的排序规则是按照数据类型本身的排序规则,基本类型默认是升序。

形如other.cmp(slef)的排序规则则是反过来的顺序。

调用代码:

let mut v:Vec<Node>=Vec::new();
......
v.sort_by(|a,b| a.cmp(b));

注意,自己实现Ord但是PartialOrd用默认的话,在排序里面的逻辑也是对的,但是以后如果有别人把这个结构体用于排序之外的场景,可能就有BUG了。

2,二分

(1)vector二分

fn search(nums: &Vec<i32>, target:i32)->i32{
    let res = nums.binary_search(&target);
    match res{
        Ok(x)=>x as i32,
        _=>-1
    }
}
fn main() {
    let nums =vec![1,2,4,5];
    println!("{}",search(&nums,1));
	println!("{}",search(&nums,4));
	println!("{}",search(&nums,7));
}

输出:

0
2
-1

非排序数组调用binary_search也能运行,但可能找不到结果。

降序数组和非排序数组同理。

有重复数的情况下,找到任意一个就直接结束。

(2)自定义二分

struct SearchData{
    target:i32
}
impl SearchData{
    fn find(&self, low:i32,high:i32)-> i32{
        let mut low = low;
        let mut high = high;
        if self.check_value(high) == false{
            return high+1;
        }
        if self.check_value(low) == true{
            return low;
        }
        while high - low > 1 {
            let mid = (high + low)/2;
            if self.check_value(mid) == true{
                high = mid;
            }
            else {
                low = mid;
            }
        }
        return high;
    }
    fn check_value(&self,x:i32)->bool{
        if x*x*x>self.target{
            return true;
        }
        return false;
    }
}
fn main() {
    let x=SearchData{target:100};
    println!("{}",x.find(0,50));
}

输出5,即5是满足x*x*x>target的最小值。

3,哈希算法

rust提供的哈希方案,是把任意数据结构转换成u8整数序列,而整数序列再次哈希的方案一般就采用默认方案。

(1)std::hash::Hash

pub trait Hash {
    fn hash<H: Hasher>(&self, state: &mut H);
    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) where
        Self: Sized,
    {
        for piece in data {
            piece.hash(state)
        }
    }
}

其中是hash函数,就是往state里面依次注册整数。

state是一个具有Hasher特征的H类型的数据。

hash_slice就是调用hash函数,把数组的每个成员,依次转换成整数注册到state里面。

示例:

struct OrderAmbivalentPair<T: Ord>(T, T);
impl<T: Ord + Hash> Hash for OrderAmbivalentPair<T> {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        min(&self.0, &self.1).hash(hasher);
        max(&self.0, &self.1).hash(hasher);
    }
}

(2)std::hash::Hasher

pub trait Hasher {
    fn finish(&self) -> u64;

    fn write(&mut self, bytes: &[u8]);

    fn write_u8(&mut self, i: u8) {
        self.write(&[i])
    }
    ......
}

Hasher中提供各种类型数据的write***函数,其他的wtite***函数也都是调用write函数,最终都是生成一个u8的整数序列。

最后再调用finish函数即可得到一个u64的整数。

(3)Hash中的hash函数

常见类型的hash函数:

macro_rules! impl_write {
        ($(($ty:ident, $meth:ident),)*) => {$(
            #[stable(feature = "rust1", since = "1.0.0")]
            impl Hash for $ty {
                #[inline]
                fn hash<H: Hasher>(&self, state: &mut H) {
                    state.$meth(*self)
                }

                #[inline]
                fn hash_slice<H: Hasher>(data: &[$ty], state: &mut H) {
                    let newlen = mem::size_of_val(data);
                    let ptr = data.as_ptr() as *const u8;
                    // SAFETY: `ptr` is valid and aligned, as this macro is only used
                    // for numeric primitives which have no padding. The new slice only
                    // spans across `data` and is never mutated, and its total size is the
                    // same as the original `data` so it can't be over `isize::MAX`.
                    state.write(unsafe { slice::from_raw_parts(ptr, newlen) })
                }
            }
        )*}
    }

    impl_write! {
        (u8, write_u8),
        (u16, write_u16),
        (u32, write_u32),
        (u64, write_u64),
        (usize, write_usize),
        (i8, write_i8),
        (i16, write_i16),
        (i32, write_i32),
        (i64, write_i64),
        (isize, write_isize),
        (u128, write_u128),
        (i128, write_i128),
    }

    #[stable(feature = "rust1", since = "1.0.0")]
    impl Hash for bool {
        #[inline]
        fn hash<H: Hasher>(&self, state: &mut H) {
            state.write_u8(*self as u8)
        }
    }

    #[stable(feature = "rust1", since = "1.0.0")]
    impl Hash for char {
        #[inline]
        fn hash<H: Hasher>(&self, state: &mut H) {
            state.write_u32(*self as u32)
        }
    }

    #[stable(feature = "rust1", since = "1.0.0")]
    impl Hash for str {
        #[inline]
        fn hash<H: Hasher>(&self, state: &mut H) {
            state.write_str(self);
        }
    }

(4)BuildHasher

相当于Hasher的再次包装

pub trait BuildHasher {
    /// Type of the hasher that will be created.
    #[stable(since = "1.7.0", feature = "build_hasher")]
    type Hasher: Hasher;

    #[stable(since = "1.7.0", feature = "build_hasher")]
    fn build_hasher(&self) -> Self::Hasher;

    #[stable(feature = "build_hasher_simple_hash_one", since = "1.71.0")]
    fn hash_one<T: Hash>(&self, x: T) -> u64
    where
        Self: Sized,
        Self::Hasher: Hasher,
    {
        let mut hasher = self.build_hasher();
        x.hash(&mut hasher);
        hasher.finish()
    }
}

(5)特征一致性

Hash特征应该和PartialEq特征保持一致,即当a==b时hash(a)==hash(b)

所以在自定义等价关系时,等价的2个数据转换后的u8序列也应该相同

4,BFS

以有向图多起点BFS洪泛为例:

//有向图多起点BFS洪泛,输入起点集v和邻接表m_next,输出经过可达的所有节点集v
//m_next的key是完整的, value可以是空列表
fn bfs(v:& mut Vec<i32>, m_next:& mut HashMap<i32,Vec<i32>>)-> (){
    let mut m=HashMap::new();
    let mut deq=VecDeque::new();
    for i in 0..v.len(){
        m.insert(v[i], 1);
        deq.push_back(v[i]);
    }
    loop {
        if deq.is_empty(){
            break;
        }
        let id = deq.pop_front().unwrap();
        for i in m_next.get_mut(&id).unwrap(){
            if m.get_mut(&i) == None{
                v.push(*i);
                m.insert(*i, 1);
                deq.push_back(*i);
            }
        }
    }
}

5,并查集

一个并查集的示例:

struct Union{
    fa:Vec<usize>,
}
impl Union{
    fn find(& mut self,x:usize)->usize{
        if self.fa[x] == x{
            return x;
        }
        Union::find(self,self.fa[x]);
        self.fa[x] = self.fa[self.fa[x]];
        return self.fa[x];
    }
    fn in_same(& mut self,x:usize,y:usize)->bool{
        return Union::find(self,x) == Union::find(self,y);
    }
    fn merge(& mut self,x:usize,y:usize)->(){
        Union::find(self,x);
        let fax = self.fa[x];
        self.fa[fax] = y;
    }
}

一个带权并查集的示例:

struct Union{
    fa:Vec<usize>,
    dif:Vec<i32>,
}
impl Union{
    fn find(& mut self,x:usize)->usize{
        if self.fa[x] == x{
            return x;
        }
        Union::find(self,self.fa[x]);
        self.dif[x] += self.dif[self.fa[x]];
        self.fa[x] = self.fa[self.fa[x]];
        return self.fa[x];
    }
    fn in_same(& mut self,x:usize,y:usize)->bool{
        return Union::find(self,x) == Union::find(self,y);
    }
    fn merge(& mut self,x:usize,y:usize,x_sub_y:i32)->(){
        Union::find(self,x);
        let fax = self.fa[x];
        self.dif[fax] = x_sub_y - self.dif[x];
        self.fa[fax] = y;
    }
}

六,通用函数

1,new函数

除了智能指针之外,其他的new都是创建空对象。

    let x=Box::new(1);
    let x=RefCell::new(3);
    let x=Rc::new(4);
    let x:Vec<i32>=Vec::new();
    let x:VecDeque<i32>=VecDeque::new();
    let x:LinkedList<i32>=LinkedList::new();
    let x=String::new();

2,from

对于整数类型,from可以往更大的类型转,不能往更小的类型转。

    let x=i32::from(1);
    let y=i64::from(x);
    let z=i64::from(y);
    let nums=Vec::from([1,2,4,3]);
    let deq = VecDeque::from([-1, 0, 1]);
    let list = LinkedList::from([1,2,4,3]);
    let s=String::from("123");

七,二元关系特征

离散数学中的二元关系

在c++的stl中,只需要2个关系,分别可以用<和==表示,其中<是序关系,==是等价关系。

rust中的二元关系更多更细致,我们一个个分析。

0,继承、一致性大前提

Ord继承了PartialOrd和Eq,而PartialOrd和Eq都继承了PartialEq

这是一个钻石环,不过trait没有构造和析构过程,所以也不在乎钻石环。

由于继承关系的存在,父特征和子特征可以对同一关系做不同的实现,但这是一种很不好的写法。

rust建议一:保持父特征和子特征中二元关系的一致性,是一个默认的大前提

PS:本章列举的rust建议,都是强烈建议,不遵守的代码可能可以编译运行,可能逻辑也暂时是对的,但一定不是好代码。

1,std::cmp::PartialEq、lhs、rhs

rust建议二:PartialEq满足对称性、传递性,即PartialEq就是部分等价关系。

rust建议三:eq和ne保持对偶,即永远不要重写ne函数

eq就是==,ne就是!=

pub trait PartialEq<Rhs: ?Sized = Self> {
    /// This method tests for `self` and `other` values to be equal, and is used
    /// by `==`.
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn eq(&self, other: &Rhs) -> bool;

    /// This method tests for `!=`. The default implementation is almost always
    /// sufficient, and should not be overridden without very good reason.
    #[inline]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn ne(&self, other: &Rhs) -> bool {
        !self.eq(other)
    }
}

lhs是left-hand side,rhs是right-hand side

PartialEq的泛型参数是rhs,默认是self,意思是默认和同类型数据进行比较。

如果用于不同类型的比较,要想满足对称性,2个PartialEq特征都要实现,且需要保持逻辑一致(对偶)

2,std::cmp::Eq

rust建议四:Eq满足自反性、对称性、传递性,即Eq就是等价关系。

This means, that in addition to a == b and a != b being strict inverses, the equality must be (for all a, b and c):

  • reflexive: a == a;
  • symmetric: a == b implies b == a; and
  • transitive: a == b and b == c implies a == c.
pub trait Eq: PartialEq<Self> {
    // this method is used solely by #[deriving] to assert
    // that every component of a type implements #[deriving]
    // itself, the current deriving infrastructure means doing this
    // assertion without using a method on this trait is nearly
    // impossible.
    //
    // This should never be implemented by hand.
    #[doc(hidden)]
    #[no_coverage] // rust-lang/rust#84605
    #[inline]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn assert_receiver_is_total_eq(&self) {}
}

例如:

f32就是满足部分等价关系,如果把f32里面的NAN去掉,得到的就是等价关系。

assert_ne!(NAN, NAN);

我们很容易有2个疑问:

(1)从源码可以看出来,Eq是非泛型的,PartialEq是泛型的,为什么这么设计呢?

因为不同类型的2个元素进行比较,和自反性无关,需要声明自反性的场景一定是同类型数据进行比较的场景。相当于PartialEq泛型是为了更好用,Eq非泛型是为了更简洁。

(2)Eq和PartialEq的方法是一样的,都是==和!=2个运算符,为什么还要区分2个特征呢?

因为==运算符无法说明自身是等价关系还是部分等价关系,当我们把PartialEq实现成部分等价关系时,建议Eq是不存在的,当我们把PartialEq实现成等价关系时,建议把默认Eq也声明出来

3,std::cmp::PartialOrd

pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
    /// This method returns an ordering between `self` and `other` values if one exists.
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;

    /// This method tests less than (for `self` and `other`) and is used by the `<` operator.
    #[inline]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn lt(&self, other: &Rhs) -> bool {
        matches!(self.partial_cmp(other), Some(Less))
    }

    /// This method tests less than or equal to (for `self` and `other`) and is used by the `<=`
    #[inline]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn le(&self, other: &Rhs) -> bool {
        matches!(self.partial_cmp(other), Some(Less | Equal))
    }

    /// This method tests greater than (for `self` and `other`) and is used by the `>` operator.
    #[inline]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn gt(&self, other: &Rhs) -> bool {
        matches!(self.partial_cmp(other), Some(Greater))
    }

    /// This method tests greater than or equal to (for `self` and `other`) and is used by the `>=`
    #[inline]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn ge(&self, other: &Rhs) -> bool {
        matches!(self.partial_cmp(other), Some(Greater | Equal))
    }
}

partial_cmp有4种结果,分别是Ordering的3个枚举值,和None,表示无法比较顺序。

PartialOrd的另外4个函数,默认实现都是直接调用partial_cmp得到的,分别是<   <=   >   >=

从源码注释和官方文档可以看到rust建议五:PartialOrd应该满足如下三条特性

  • irreflexivity of < and >: !(a < a), !(a > a)
  • transitivity of >: if a > b and b > c then a > c
  • duality of partial_cmp: partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)

4,PartialOrd的二元关系推导

由于这一块内容比较复杂,所以我单独写成一节。

(0)一致性大前提

partial_cmp返回Equal    等价于    父类的eq返回true

partial_cmp返回另外3个值    等价于    父类的eq返回false

所以,==关系具有对称性、传递性

(1)对偶性

PartialOrd满足 对偶性,即a<b等价于b>a   a==b等价于b==a   a>b等价于b<a

(2)反自反

PartialOrd满足 大于关系的反自反、小于关系的反自反

(3)传递性

PartialOrd满足 大于关系的传递性

通过对偶性和大于关系的传递性,可以推出小于关系也具有传递性。

(4)对称性

通过反自反和传递性可以推出,大于关系和小于关系都具有反对称性,不具有对称性。

根据源码和官方文档,只能推导出上面的这些信息,总结起来就是两句话:

PartialOrd中的 < 和 > 都是拟序,且2个关系互为对偶,且都和部分等价关系没有交集。

PartialOrd中的 <= 和 >= 指的是对应的拟序关系和部分等价关系的并集,<= 和 >=互为对偶。

实际上这些条件是不够的,里面仍然包含了一些非常复杂的情况。

(5)关系之间的传递性

根据实际情况,我认为实际上是有rust建议六:partial_cmp最好满足如下4条传递性:

  • 若a>b b==c则a>c
  • 若a<b b==c则a<c
  • 若a==b b>c则a>c
  • 若a==b b<c则a<c

根据前面的性质和新增的这4条传递性,可以推出<=和>=也具有传递性。

(6)等价自反子集

等价自反子集:由满足a==a的所有的a构成的集合。

例如,对于float集合,去掉NAN得到的就是等价自反子集。

显然,对于一个具有部分等价关系的集合,它的等价自反子集一定具有等价关系

(7)非自反元素的隔离

rust建议七:如果a!=a,则a和任意元素的partial_cmp都返回None

例如,f32类型就是满足建议六和建议七的。

(8)等价自反子集的映射子集上的偏序

实际上,即使加上了建议六和建议七,<=仍然不是等价自反子集上的偏序关系。

示例:

#[derive(Eq,PartialEq,Ord)]
struct Node{
    x:i32,
    y:i32,
}
impl PartialOrd for Node {
    #[inline]
    fn partial_cmp(&self, other: &Node) ->  Option<Ordering> {
        if self.x < (*other).x {
            return Some(Ordering::Less);
        }
        if self.x > (*other).x {
            return Some(Ordering::Greater);
        }
        if self.y <= 0 && (*other).y <= 0 {
            return Some(Ordering::Equal);
        }
        if self.y > 0 && (*other).y > 0 {
            return Some(Ordering::Equal);
        }
        return None;
    }
}

这还是一个比较简单的例子,还有很多更复杂的例子都满足上面的所有性质和建议。

一般情况下,在讨论自反性时,我们说a和b是同一个元素,指的是a和b是同一个内存地址中的数据,满足等价关系或者完全相等的数值并不代表是同一个元素。

所以,我们首先把等价自反子集做一个映射,把所有等价的元素映射到一个元素上,得到等价自反子集的映射子集。

这样,我们就可以说,在等价自反子集的映射子集上,PartialOrd中的<=是偏序

(9)示例

#[derive(Eq,PartialEq,Ord)]
struct Node2{
    x:i32,
    y:i32,
}
impl PartialOrd for Node2 {
    #[inline]
    fn partial_cmp(&self, other: &Node2) ->  Option<Ordering> {
        if self.x == 0 || (*other).x == 0{
            return None;
        }
        if self.x < (*other).x {
            return Some(Ordering::Less);
        }
        if self.x > (*other).x {
            return Some(Ordering::Greater);
        }
        if self.y <= 0 && (*other).y <= 0 {
            return Some(Ordering::Equal);
        }
        if self.y > 0 && (*other).y > 0 {
            return Some(Ordering::Equal);
        }
        return None;
    }
}

在这个例子中,集合是Node2的定义域,即全体平面{(x,y) | x是整数,y是整数}。这里我们忽略i32的上下界,把它理解成全体整数。

等价自反子集是{(x,y) | x是整数,x!=0,y是整数}

等价自反子集的映射子集是{(x,z) | x是整数,x!=0,z=0或1},映射方式是y<=0则z=0,y>0则z=1

那么Node2::partial_cmp在这个映射子集上的关系就是偏序。

(10)不同类型数据之间的PartialOrd

PartialOrd和PartialEq类似,支持泛型。

如果用于不同类型的比较,要想满足对称性,2个PartialEq特征都要实现,且需要保持逻辑一致(对偶)

5,std::cmp::Ord

pub trait Ord: Eq + PartialOrd<Self> {
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn cmp(&self, other: &Self) -> Ordering;
    ......
}

(1)数据类型、泛型

和Eq类似,Ord不支持泛型,即Ord只用于比较同类型数据。

(2)cmp的二元关系

(2.1)for all `a`, `b` and `c`: exactly one of `a < b`, `a == b` or `a > b` is true; 这个是强制要求,因为cmp的返回值类型是Ordering,而不是Option。

(2.2)根据rust建议一:保持父特征和子特征中二元关系的一致性,可以推出一个恒等式:partial_cmp(a, b) == Some(cmp(a, b)),这也是源码和官方文档中给出的式子。

(2.3)根据(2.1)和(2.2)以及partial_cmp本身蕴含的诸多条件,可以推出,cmp中的<就是严格弱序,<=就是全序

(3)PartialOrd和Ord的选择

上文提到:当我们把PartialEq实现成部分等价关系时,建议Eq是不存在的,当我们把PartialEq实现成等价关系时,建议把默认Eq也声明出来。

PartialOrd和Ord也是类似的,当我们把PartialOrd(中的<=)实现成全序时,建议把Ord也实现出来,否则,建议Ord是不存在的

PS:为什么不给cmp添加默认实现,由partial_cmp推导出cmp呢?参考下文,实际上应该用cmp推导出partial_cmp,所以2个函数都没有默认实现。

6,默认序关系

(1)基本类型

整数类型都有默认的全序关系,1<=2<=3

浮点数有默认的偏序关系,1<=1.5<=2

其他基本类型同理。

(2)结构体的默认序关系

如果结构体用derive声明默认的PartialOrd或者Ord,则按照字典序推导出序关系。

当然,能编译通过的前提是所有成员都有相应的序关系。

(3)容器的默认序关系

以vec为例,默认序关系就是字典序:

/// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison).
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, A1, A2> PartialOrd<Vec<T, A2>> for Vec<T, A1>
where
    T: PartialOrd,
    A1: Allocator,
    A2: Allocator,
{
    #[inline]
    fn partial_cmp(&self, other: &Vec<T, A2>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Eq, A: Allocator> Eq for Vec<T, A> {}

/// Implements ordering of vectors, [lexicographically](Ord#lexicographical-comparison).
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Ord, A: Allocator> Ord for Vec<T, A> {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

7,自定义序关系的场景

本节内容只讨论同类型数据之间的关系处理原则,不同类型之间没什么要注意的。

(1)PartialEq和Eq

正常情况下,我们都应该实现PartialEq特征,而Eq特征虽然继承了PartialEq但并没有自己的方法需要实现。

(2)偏序的PartialOrd

如果实现一个偏序,则只实现PartialOrd,无论把PartialOrd实现成什么样,都建议Ord是不存在的

比如,这里我实现了PartialOrd,又声明了默认的Ord,则会造成不一致:

#[derive(Eq,PartialEq,Ord)]
struct Node{
    num:i32
}
impl PartialOrd for Node {
    #[inline]
    fn partial_cmp(&self, other: &Node) ->  Option<Ordering> {
        if self.num == 0 || (*other).num == 0 {
            return None;
        }
        return (*other).num.partial_cmp(&self.num);
    }
}
fn main() {
    let x = Node{num:1};
    let y = Node{num:3};
    assert_eq!(x.partial_cmp(&y),Some(Ordering::Greater));
    assert_eq!(x.cmp(&y),Ordering::Less);
    println!("end");
}

(3)全序的PartialOrd和Ord

当我们把PartialOrd实现成去全序时,建议一律采用如下的固定实现方式,即只调用cmp推导出partial_cmp

impl PartialOrd for Node {
    #[inline]
    fn partial_cmp(&self, other: &Node) ->  Option<Ordering> {
        return Some(self.cmp(other));
    }
}

例如,全序的二叉堆,虽然源码只会调用<=函数,即只会调用partial_cmp而不会调用cmp,但是二叉堆的数据类型必须是含有Ord特征的类型,f32就编译不过。

因为,有Ord特征就暗示了是全序关系,PartialOrd特征没法说明自身是否是全序,就不能完全满足堆的需求。

所以,最好就是直接实现cmp,然后用cmp推导出partial_cmp。

同理,自定义排序,虽然源码只会调用cmp而不会调用partial_cmp,但最好就是直接实现cmp,然后用cmp推导出partial_cmp。

(4)偏序集的等价自反子集

例如,我要用浮点数作为堆的类型,而且我知道肯定不会有NAN的出现,那么可以这么写:

#[derive(PartialEq,PartialOrd)]
struct FloatWithOrd{
    x:f32, // do not let x be NAN
}
impl Eq for FloatWithOrd{
}
impl Ord for FloatWithOrd {
    #[inline]
    fn cmp(&self,other:&FloatWithOrd)->Ordering{
        if(self.x < other.x){
            return Ordering::Less;
        }
        if(self.x > other.x){
            return Ordering::Greater;
        }
        return Ordering::Equal;
    }
}
fn main() {
    let mut heap = BinaryHeap::new();
    heap.push(FloatWithOrd{x:1.5});
    heap.push(FloatWithOrd{x:3.0});
    assert_eq!(heap.peek().unwrap().x, 3.0);
    println!("end");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值