目录

use std::option;
(1)定义
- pub enum Option
{ - None,
- Some(T),
- }
这是一个枚举值,要么等于整型的None,要么等于泛型的只含一个元素的元组Some
(2)is_some、is_none
- let x:Option
=Some(2); - assert_eq!(x.is_some(),true);
- assert_eq!(x.is_none(),false);
- let x:Option
=None; - assert_eq!(x.is_some(),false);
- assert_eq!(x.is_none(),true);
(3)is_some_and
- let x:Option
=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);
use std::result;
(1)定义
- pub enum Result
{ - Ok(T),
- Err(E),
- }
这也是个枚举值,要么是等于泛型的元组OK,要么是等于泛型的元组Err
2个模板类型可以一样也可以不一样。
(2)is_ok、is_err
- let x:Result
=Ok(1); - assert_eq!(x.is_ok(),true);
- assert_eq!(x.is_err(),false);
- let x:Result
=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
=Ok(1); - assert_eq!(x.is_ok_and(|x| x==1),true);
- assert_eq!(x.is_err(),false);
- let x:Result
=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
=Ok(1); - assert_eq!(x.ok(),Some(1));
- assert_eq!(x.err(),None);
- let x:Result
=Err("456"); - assert_eq!(x.ok(),None);
- assert_eq!(x.err(),Some("456"));
(5)unwrap
- let x:Result
=Ok(1); - assert_eq!(x.unwrap(),1);
- assert_eq!(x.ok().unwrap(),1);
(6)map
源码:
- pub fn map U>(self, op: F) -> Result {
- match self {
- Ok(t) => Ok(op(t)),
- Err(e) => Err(e),
- }
- }
示例:
- fn main() {
- let x:Result
=Ok(1); - let x=x.map(|x| x+1);
- assert_eq!(x,Ok(2));
- let x:Result
=Err(1); - let x=x.map(|x| x+1);
- assert_eq!(x,Err(1));
- print!("end ");
- }
因为Result的map方法是只对Ok生效的
use std::vec::Vec;
(1)用作vector
- let nums:Vec
=vec![1,2,4,3]; - assert_eq!(nums.len(),4);
- assert_eq!(nums[3],3);
- assert_eq!(nums.is_empty(),false);
遍历:
- let mut nums:Vec
=vec![1,2,4,3]; - for i in 0..nums.len() {
- nums[i]=0;
- }
- assert_eq!(nums, vec![0,0,0,0]);
- for x in nums.iter_mut(){
- *x=1;
- }
- assert_eq!(nums, vec![1,1,1,1]);
- for mut x in nums{
- x=1;
- }
- // assert_eq!(nums, vec![1,1,1,1]); //error,nums丧失了所有权
vector翻转:
- 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;
- }
(2)用作栈
- let mut nums:Vec
=Vec::new(); - nums.push(123);
- assert_eq!(nums.len(),1);
- assert_eq!(nums.last(),Some(&123));
- assert_eq!(nums.len(),1);
- assert_eq!(nums.pop(),Some(123));
- assert_eq!(nums.len(),0);
(3)实现原理
- pub(crate) struct RawVec
{ - ptr: Unique
, - cap: usize,
- alloc: A,
- }
- pub struct Vec
unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { - buf: RawVec
, - len: usize,
- }
buf中存放的是指针和最大可存储元素个数,len是实际元素个数。
例如push函数:
- pub fn push(&mut self, value: T) {
- if self.len == self.buf.capacity() {
- self.buf.reserve_for_push(self.len);
- }
- unsafe {
- let end = self.as_mut_ptr().add(self.len);
- ptr::write(end, value);
- self.len += 1;
- }
- }
每次push都检查len和cap,扩容方案是倍增,同C++。
reserve函数:
- pub fn reserve(&mut self, additional: usize) {
- self.buf.reserve(self.len, additional);
- }
-
- let mut v = Vec::from([1]);
- v.reserve(10);
- assert_eq!(v.capacity(), 11);
- assert_eq!(v.len(), 1);
Rust::reverse(n)等价于C++STL::reserve(n+ v.capacity())
resize函数:
- pub fn resize(&mut self, new_len: usize, value: T) {
- let len = self.len();
- if new_len > len {
- self.extend_with(new_len - len, value)
- } else {
- self.truncate(new_len);
- }
- }
-
-
- let mut vec = vec!["hello"];
- vec.resize(3, "world");
- assert_eq!(vec, ["hello", "world", "world"]);
- let mut vec = vec![1, 2, 3, 4];
- vec.resize(2, 0);
- assert_eq!(vec, [1, 2]);
resize函数和C++完全相同。
use std::collections::VecDeque;
(1)用作队列
new创建空队列
len获取长度
with_capacity创建空队列 但 预占内存
push_back是尾部插入
pop_front是头部弹出,并获取弹出值
- let deq:VecDeque
= VecDeque::new(); - assert_eq!(deq.len(), 0);
- let mut deq:VecDeque
= VecDeque::with_capacity(10); - assert_eq!(deq.len(), 0);
- deq.push_back(1);
- deq.push_back(2);
- assert_eq!(deq.pop_front(),Some(1));
- assert_eq!(deq.len(), 1);
(2)用作双端队列
from从列表创建队列
get获取任意位置的值
push_front头部插入
pop_back尾部弹出,并获取弹出值
2个队列还可以直接比较
- let mut deq = VecDeque::from([-1, 0, 1]);
- assert_eq!(deq.get(2),Some(&1));
- deq.push_front(2);
- assert_eq!(deq.pop_back(),Some(1));
- let deq2 = VecDeque::from([2, -1, 0]);
- assert_eq!(deq,deq2);
- deq.pop_back();
- assert_ne!(deq,deq2);
(3)实现原理
- pub struct VecDeque
{ - head: usize,
- len: usize,
- buf: RawVec
, - }
不像c++采用分段数组,rust的双端队列采用的是循环数组。
扩容方案:
- fn grow(&mut self) {
- // Extend or possibly remove this assertion when valid use-cases for growing the
- // buffer without it being full emerge
- debug_assert!(self.is_full());
- let old_cap = self.capacity();
- self.buf.reserve_for_push(old_cap);
- unsafe {
- self.handle_capacity_increase(old_cap);
- }
- debug_assert!(!self.is_full());
- }
和Vec一样,采用倍增的扩容方案。
这里的扩容稍微麻烦一点,先reserve_for_push倍增拷贝所有数据,然后handle_capacity_increase再拷贝部分数据(最多一半),调整head和tail
use std::collections::LinkedList;
(1)用法
支持在头部和尾部插入节点,在任意位置删除节点。
- let mut list = LinkedList::new();
- list.push_back(2);
- list.push_back(3);
- list.push_front(1);
- // list is 1->2->3
- assert_eq!(list.remove(1), 2);
- assert_eq!(list.remove(0), 1);
- assert_eq!(list.remove(0), 3);
与其说这是双向链表,不如说这个有点像数组。
(2)源码
- pub struct LinkedList
{ - head: Option
>>, - tail: Option
>>, - len: usize,
- alloc: A,
- marker: PhantomData
, A>>, - }
-
- struct Node
{ - next: Option
>>, - prev: Option
>>, - element: T,
- }
use std::collections::HashMap;
use std::collections::BTreeMap;
2个哈希表的常用方法相同:
- let mut m = HashMap::new();
- if let Some(p) = m.get_mut(&1){
- *p += 1;
- }else{
- m.insert(1, 1);
- }
-
- let mut m = BTreeMap::new();
- if let Some(p) = m.get_mut(&1){
- *p += 1;
- }else{
- m.insert(1, 1);
- }
例如,实现一个基于去重计数功能的类:
- struct CalNum{
- m:HashMap
, - }
- impl CalNum{
- fn add(& mut self,x:i32)->i32{
- if let Some(p)=self.m.get_mut(&x){
- *p+=1;
- }else{
- self.m.insert(x,1);
- }
- return self.m.len() as i32;
- }
- fn sub(& mut self,x:i32)->i32{
- if let Some(p)=self.m.get_mut(&x){
- *p-=1;
- if *p <= 0{
- self.m.remove(&x);
- }
- }
- return self.m.len() as i32;
- }
- fn get(& mut self)->i32{
- return self.m.len() as i32;
- }
- }
use std::collections::HashSet;
use std::collections::BTreeSet;
(1)常用方法
2个集合的常用方法完全相同。
HashSet:
- let mut m = HashSet::new();
- m.insert(5);
- assert_eq!(m.len(), 1);
- if !m.contains(&6) {
- m.insert(6);
- }
- assert_eq!(m.len(), 2);
- m.insert(6);
- assert_eq!(m.len(), 2);
- m.remove(&5);
- m.remove(&6);
- assert_eq!(m.is_empty(), true);
BTreeSet:
- let mut m = BTreeSet::new();
- m.insert(5);
- assert_eq!(m.len(), 1);
- if !m.contains(&6) {
- m.insert(6);
- }
- assert_eq!(m.len(), 2);
- m.insert(6);
- assert_eq!(m.len(), 2);
- m.remove(&5);
- m.remove(&6);
- assert_eq!(m.is_empty(), true);
(2)数据类型约束
- impl
HashSet - where
- T: Eq + Hash,
- S: BuildHasher,
- {
- ......
- }
HashSet的泛型实现就约束了T具有Eq和Hash
- impl
BTreeSet { - ......
- pub fn contains
(&self, value: &Q) -> bool - where
- T: Borrow
+ Ord,
- Q: Ord,
- {
- self.map.contains_key(value)
- }
- ......
- pub fn insert(&mut self, value: T) -> bool
- where
- T: Ord,
- {
- self.map.insert(value, SetValZST::default()).is_none()
- }
- ......
- }
BTreeSet的泛型实现约束较少,但是常见的接口需要Ord特征。
(3)浮点数示例
如果确保浮点数不会等于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 m = BTreeSet::new();
- m.insert(FloatWithOrd{x:5.0});
- assert_eq!(m.len(), 1);
- if !m.contains(&FloatWithOrd{x:6.0}) {
- m.insert(FloatWithOrd{x:6.0});
- }
- assert_eq!(m.len(), 2);
- m.insert(FloatWithOrd{x:6.0});
- assert_eq!(m.len(), 2);
- m.remove(&FloatWithOrd{x:5.0});
- m.remove(&FloatWithOrd{x:6.0});
- assert_eq!(m.is_empty(), true);
- println!("end");
- }
浮点数类型的HashSet比较麻烦,需要Hash,暂时不研究这个。
use std::collections::BinaryHeap;
看实现源码是用二叉堆实现的。
默认堆顶是最大值:
- let mut heap = BinaryHeap::new();
- heap.push(1);
- heap.push(3);
- heap.push(1);
- heap.push(2);
- assert_eq!(heap.peek(),Some(&3));
- heap.pop();
- assert_eq!(heap.peek(),Some(&2));
- heap.pop();
- assert_eq!(heap.peek(),Some(&1));
- heap.pop();
- assert_eq!(heap.peek(),Some(&1));
- heap.pop();
- assert_eq!(heap.peek(),None);
要想使用堆顶是最小值的堆,有2种方法:自定义序关系、reserve
(1)自定义序关系
- #[derive(Eq,PartialEq)]
- struct Node{
- num:i32
- }
- impl Ord for Node {
- #[inline]
- fn cmp(&self,other:&Node)->Ordering{
- return other.num.cmp(&self.num);
- }
- }
- impl PartialOrd for Node {
- #[inline]
- fn partial_cmp(&self, other: &Node) -> Option
{ - return Some(self.cmp(other));
- }
- }
- fn main() {
- let mut heap = BinaryHeap::new();
- heap.push(Node{num:1});
- heap.push(Node{num:3});
- assert_eq!(heap.peek().unwrap().num, 1);
- println!("end");
- }
注意,自己实现PartialOrd但是Ord用默认的话,在堆里面的逻辑也是对的,但是以后如果有别人把这个结构体用于堆之外的场景,可能就有BUG了。
(2)用reserve
- use std::cmp::Reverse;
- fn main() {
- let mut heap = BinaryHeap::new();
- heap.push(Reverse(1));
- heap.push(Reverse(3));
- assert_eq!(heap.peek().unwrap(), &Reverse(1));
- assert_eq!(Reverse(1)>Reverse(3), true);
- println!("end");
- }
reserve就像是一个加负号的技巧:
- fn main() {
- let mut heap = BinaryHeap::new();
- heap.push(-1);
- heap.push(-3);
- assert_eq!(heap.peek().unwrap(), &-1);
- assert_eq!(-1>-3, true);
- println!("end");
- }
(3)堆排序
BinaryHeap自带堆排序into_sorted_vec
(4)源码解析
虽然BinaryHeap的定义本身没有要求ord特征,但是默认实现的泛型方法要求了Ord特征。
这里我摘录了核心的几个函数:pop、push、into_sorted_vec,以及其中调用的关键操作。
- pub struct BinaryHeap< T,A: Allocator = Global,> {
- data: Vec
, - }
-
- impl
BinaryHeap { - 。。。。。。
- pub fn pop(&mut self) -> Option
{ - self.data.pop().map(|mut item| {
- if !self.is_empty() {
- swap(&mut item, &mut self.data[0]);
- // SAFETY: !self.is_empty() means that self.len() > 0
- unsafe { self.sift_down_to_bottom(0) };
- }
- item
- })
- }
- pub fn push(&mut self, item: T) {
- let old_len = self.len();
- self.data.push(item);
- // SAFETY: Since we pushed a new item it means that
- // old_len = self.len() - 1 < self.len()
- unsafe { self.sift_up(0, old_len) };
- }
- pub fn into_sorted_vec(mut self) -> Vec
{ - let mut end = self.len();
- while end > 1 {
- end -= 1;
- // SAFETY: `end` goes from `self.len() - 1` to 1 (both included),
- // so it's always a valid index to access.
- // It is safe to access index 0 (i.e. `ptr`), because
- // 1 <= end < self.len(), which means self.len() >= 2.
- unsafe {
- let ptr = self.data.as_mut_ptr();
- ptr::swap(ptr, ptr.add(end));
- }
- // SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so:
- // 0 < 1 <= end <= self.len() - 1 < self.len()
- // Which means 0 < end and end < self.len().
- unsafe { self.sift_down_range(0, end) };
- }
- self.into_vec()
- }
- unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
- // Take out the value at `pos` and create a hole.
- // SAFETY: The caller guarantees that pos < self.len()
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
-
- while hole.pos() > start {
- let parent = (hole.pos() - 1) / 2;
-
- // SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
- // and so hole.pos() - 1 can't underflow.
- // This guarantees that parent < hole.pos() so
- // it's a valid index and also != hole.pos().
- if hole.element() <= unsafe { hole.get(parent) } {
- break;
- }
-
- // SAFETY: Same as above
- unsafe { hole.move_to(parent) };
- }
-
- hole.pos()
- }
- unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
- // SAFETY: The caller guarantees that pos < end <= self.len().
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
- let mut child = 2 * hole.pos() + 1;
-
- // Loop invariant: child == 2 * hole.pos() + 1.
- while child <= end.saturating_sub(2) {
- // compare with the greater of the two children
- // SAFETY: child < end - 1 < self.len() and
- // child + 1 < end <= self.len(), so they're valid indexes.
- // child == 2 * hole.pos() + 1 != hole.pos() and
- // child + 1 == 2 * hole.pos() + 2 != hole.pos().
- // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
- // if T is a ZST
- child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
-
- // if we are already in order, stop.
- // SAFETY: child is now either the old child or the old child+1
- // We already proven that both are < self.len() and != hole.pos()
- if hole.element() >= unsafe { hole.get(child) } {
- return;
- }
-
- // SAFETY: same as above.
- unsafe { hole.move_to(child) };
- child = 2 * hole.pos() + 1;
- }
-
- // SAFETY: && short circuit, which means that in the
- // second condition it's already true that child == end - 1 < self.len().
- if child == end - 1 && hole.element() < unsafe { hole.get(child) } {
- // SAFETY: child is already proven to be a valid index and
- // child == 2 * hole.pos() + 1 != hole.pos().
- unsafe { hole.move_to(child) };
- }
- }
- unsafe fn sift_down(&mut self, pos: usize) {
- let len = self.len();
- // SAFETY: pos < len is guaranteed by the caller and
- // obviously len = self.len() <= self.len().
- unsafe { self.sift_down_range(pos, len) };
- }
-
- unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
- let end = self.len();
- let start = pos;
-
- // SAFETY: The caller guarantees that pos < self.len().
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
- let mut child = 2 * hole.pos() + 1;
-
- // Loop invariant: child == 2 * hole.pos() + 1.
- while child <= end.saturating_sub(2) {
- // SAFETY: child < end - 1 < self.len() and
- // child + 1 < end <= self.len(), so they're valid indexes.
- // child == 2 * hole.pos() + 1 != hole.pos() and
- // child + 1 == 2 * hole.pos() + 2 != hole.pos().
- // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
- // if T is a ZST
- child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
-
- // SAFETY: Same as above
- unsafe { hole.move_to(child) };
- child = 2 * hole.pos() + 1;
- }
-
- if child == end - 1 {
- // SAFETY: child == end - 1 < self.len(), so it's a valid index
- // and child == 2 * hole.pos() + 1 != hole.pos().
- unsafe { hole.move_to(child) };
- }
- pos = hole.pos();
- drop(hole);
-
- // SAFETY: pos is the position in the hole and was already proven
- // to be a valid index.
- unsafe { self.sift_up(start, pos) };
- }
- 。。。。。。
- }
sift_down_range是往下调整到指定id,主要用于堆排序
sift_down是往下调整到底,直接调用sift_down_range
sift_down_to_bottom是先往下调整到底,之后再往上调整到顶。
(5)应用实例
rust源码的注释中,给出了Dijkstra's shortest path algorithm作为示例,如何使用优先队列。
- //use std::cmp::Ordering;
- //use std::collections::BinaryHeap;
-
- #[derive(Copy, Clone, Eq, PartialEq)]
- struct State {
- cost: usize,
- position: usize,
- }
-
- // The priority queue depends on `Ord`.
- // Explicitly implement the trait so the queue becomes a min-heap
- // instead of a max-heap.
- impl Ord for State {
- fn cmp(&self, other: &Self) -> Ordering {
- // Notice that the we flip the ordering on costs.
- // In case of a tie we compare positions - this step is necessary
- // to make implementations of `PartialEq` and `Ord` consistent.
- other.cost.cmp(&self.cost)
- .then_with(|| self.position.cmp(&other.position))
- }
- }
-
- // `PartialOrd` needs to be implemented as well.
- impl PartialOrd for State {
- fn partial_cmp(&self, other: &Self) -> Option
{ - Some(self.cmp(other))
- }
- }
-
- // Each node is represented as a `usize`, for a shorter implementation.
- struct Edge {
- node: usize,
- cost: usize,
- }
-
- // Dijkstra's shortest path algorithm.
-
- // Start at `start` and use `dist` to track the current shortest distance
- // to each node. This implementation isn't memory-efficient as it may leave duplicate
- // nodes in the queue. It also uses `usize::MAX` as a sentinel value,
- // for a simpler implementation.
- fn shortest_path(adj_list: &Vec
>, start: usize, goal: usize) -> Option { - // dist[node] = current shortest distance from `start` to `node`
- let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect();
-
- let mut heap = BinaryHeap::new();
-
- // We're at `start`, with a zero cost
- dist[start] = 0;
- heap.push(State { cost: 0, position: start });
-
- // Examine the frontier with lower cost nodes first (min-heap)
- while let Some(State { cost, position }) = heap.pop() {
- // Alternatively we could have continued to find all shortest paths
- if position == goal { return Some(cost); }
-
- // Important as we may have already found a better way
- if cost > dist[position] { continue; }
-
- // For each node we can reach, see if we can find a way with
- // a lower cost going through this node
- for edge in &adj_list[position] {
- let next = State { cost: cost + edge.cost, position: edge.node };
-
- // If so, add it to the frontier and continue
- if next.cost < dist[next.position] {
- heap.push(next);
- // Relaxation, we have now found a better way
- dist[next.position] = next.cost;
- }
- }
- }
-
- // Goal not reachable
- None
- }
-
- fn main() {
- // This is the directed graph we're going to use.
- // The node numbers correspond to the different states,
- // and the edge weights symbolize the cost of moving
- // from one node to another.
- // Note that the edges are one-way.
- //
- // 7
- // +-----------------+
- // | |
- // v 1 2 | 2
- // 0 -----> 1 -----> 3 ---> 4
- // | ^ ^ ^
- // | | 1 | |
- // | | | 3 | 1
- // +------> 2 -------+ |
- // 10 | |
- // +---------------+
- //
- // The graph is represented as an adjacency list where each index,
- // corresponding to a node value, has a list of outgoing edges.
- // Chosen for its efficiency.
- let graph = vec![
- // Node 0
- vec![Edge { node: 2, cost: 10 },
- Edge { node: 1, cost: 1 }],
- // Node 1
- vec![Edge { node: 3, cost: 2 }],
- // Node 2
- vec![Edge { node: 1, cost: 1 },
- Edge { node: 3, cost: 3 },
- Edge { node: 4, cost: 1 }],
- // Node 3
- vec![Edge { node: 0, cost: 7 },
- Edge { node: 4, cost: 2 }],
- // Node 4
- vec![]];
-
- assert_eq!(shortest_path(&graph, 0, 1), Some(1));
- assert_eq!(shortest_path(&graph, 0, 3), Some(3));
- assert_eq!(shortest_path(&graph, 3, 0), Some(7));
- assert_eq!(shortest_path(&graph, 0, 4), Some(5));
- assert_eq!(shortest_path(&graph, 4, 0), None);
- println!("endend");
- }
(0)字符串编码
参考这里
(1)String的实现
use std::string::String;
- pub struct String {
- vec: Vec
, - }
(2)String和Vec
String和Vec
- 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)String和Vec
String和Vec
- let s=String::from("哈喽,世界");
- let v:Vec
= 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
也就是说,str和String一样,虽然底层实现是u8组成的,但和u8序列并不双射,而是和字符串双射。
(6)&str和Vec
- 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
- let s:&str = "hello world";
- let v:Vec
= s.as_bytes().to_vec(); - let s2 = String::from_utf8(v).unwrap();
- let s3:&str=&s2;
(8)完整互转有向图

左边2个构成u8域,右边3个构成char域,域内双射,从char域到u8域是单射非满射。
链表定义:
- #[derive(PartialEq, Eq, Clone, Debug)]
- pub struct ListNode {
- pub val: i32,
- pub next: Option
> - }
-
- impl ListNode {
- #[inline]
- fn new(val: i32) -> Self {
- ListNode {
- next: None,
- val
- }
- }
- }
单链表和vector的互转:
- fn trans_link_list_to_vector(head: Option
>) ->Vec { - 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
) -> Option> { - 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;
- }
二叉树定义:
- use std::rc::Rc;
- use std::cell::RefCell;
- #[derive(Debug, PartialEq, Eq)]
- pub struct TreeNode {
- pub val: i32,
- pub left: Option
>>, - pub right: Option
>>, - }
- impl TreeNode {
- #[inline]
- pub fn new(val: i32) -> Self {
- TreeNode {
- val,
- left: None,
- right: None
- }
- }
- }
序列化和反序列化:
-
- fn serialize(root: Option
>>, error: i32) ->Vec { - 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
, error:i32) ->Option>> { - 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::min
std::cmp::max
这是一个泛型的元组结构体,实现了泛型的逆序:
- pub struct Reverse
(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T); -
- #[stable(feature = "reverse_cmp_key", since = "1.19.0")]
- impl
PartialOrd for Reverse { - #[inline]
- fn partial_cmp(&self, other: &Reverse
) -> Option { - 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
Ord for Reverse { - #[inline]
- fn cmp(&self, other: &Reverse
) -> Ordering { - other.0.cmp(&self.0)
- }
- }
- fn main() {
- let mut nums=vec![1,2,4,3];
- nums.sort();
- println!("{}",nums[3]);
- }
vector的默认排序,从小到大排序,输出4
给自定义结构体添加自定义排序规则,需要实现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
{ - return Some(self.cmp(other));
- }
- }
Ordering是枚举值,分别表示小于等于大于。
形如self.cmp(other)的排序规则是按照数据类型本身的排序规则,基本类型默认是升序。
形如other.cmp(slef)的排序规则则是反过来的顺序。
调用代码:
- let mut v:Vec
=Vec::new(); - ......
- v.sort_by(|a,b| a.cmp(b));
注意,自己实现Ord但是PartialOrd用默认的话,在排序里面的逻辑也是对的,但是以后如果有别人把这个结构体用于排序之外的场景,可能就有BUG了。
- fn search(nums: &Vec
, 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也能运行,但可能找不到结果。
降序数组和非排序数组同理。
有重复数的情况下,找到任意一个就直接结束。
- 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的最小值。
rust提供的哈希方案,是把任意数据结构转换成u8整数序列,而整数序列再次哈希的方案一般就采用默认方案。
(1)std::hash::Hash
- pub trait Hash {
- fn hash
(&self, state: &mut H); - fn hash_slice
(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, T); - impl
Hash for OrderAmbivalentPair { - fn hash
(&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
(&self, state: &mut H) { - state.$meth(*self)
- }
-
- #[inline]
- fn hash_slice
(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
(&self, state: &mut H) { - state.write_u8(*self as u8)
- }
- }
-
- #[stable(feature = "rust1", since = "1.0.0")]
- impl Hash for char {
- #[inline]
- fn hash
(&self, state: &mut H) { - state.write_u32(*self as u32)
- }
- }
-
- #[stable(feature = "rust1", since = "1.0.0")]
- impl Hash for str {
- #[inline]
- fn hash
(&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
(&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序列也应该相同。
以有向图多起点BFS洪泛为例:
- //有向图多起点BFS洪泛,输入起点集v和邻接表m_next,输出经过可达的所有节点集v
- //m_next的key是完整的, value可以是空列表
- fn bfs(v:& mut Vec
, m_next:& mut HashMap>) -> (){ - 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);
- }
- }
- }
- }
一个并查集的示例:
- struct Union{
- fa:Vec
, - }
- 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
, - dif:Vec
, - }
- 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;
- }
- }
除了智能指针之外,其他的new都是创建空对象。
- let x=Box::new(1);
- let x=RefCell::new(3);
- let x=Rc::new(4);
- let x:Vec
=Vec::new(); - let x:VecDeque
=VecDeque::new(); - let x:LinkedList
=LinkedList::new(); - let x=String::new();
对于整数类型,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中的二元关系更多更细致,我们一个个分析。
Ord继承了PartialOrd和Eq,而PartialOrd和Eq都继承了PartialEq
这是一个钻石环,不过trait没有构造和析构过程,所以也不在乎钻石环。
由于继承关系的存在,父特征和子特征可以对同一关系做不同的实现,但这是一种很不好的写法。
rust建议一:保持父特征和子特征中二元关系的一致性,是一个默认的大前提。
PS:本章列举的rust建议,都是强烈建议,不遵守的代码可能可以编译运行,可能逻辑也暂时是对的,但一定不是好代码。
rust建议二:PartialEq满足对称性、传递性,即PartialEq就是部分等价关系。
rust建议三:eq和ne保持对偶,即永远不要重写ne函数
eq就是==,ne就是!=
- pub trait PartialEq
{ - /// 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特征都要实现,且需要保持逻辑一致(对偶)。
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):
- pub trait Eq: PartialEq
{ - // 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也声明出来。
- pub trait PartialOrd
: PartialEq { - /// 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
; -
- /// 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应该满足如下三条特性:
由于这一块内容比较复杂,所以我单独写成一节。
(0)一致性大前提
partial_cmp返回Equal 等价于 父类的eq返回true
partial_cmp返回另外3个值 等价于 父类的eq返回false
所以,==关系具有对称性、传递性
(1)对偶性
PartialOrd满足 对偶性,即aa a==b等价于b==a a>b等价于b
(2)反自反 PartialOrd满足 大于关系的反自反、小于关系的反自反 (3)传递性 PartialOrd满足 大于关系的传递性 通过对偶性和大于关系的传递性,可以推出小于关系也具有传递性。 (4)对称性 通过反自反和传递性可以推出,大于关系和小于关系都具有反对称性,不具有对称性。 根据源码和官方文档,只能推导出上面的这些信息,总结起来就是两句话: PartialOrd中的 < 和 > 都是拟序,且2个关系互为对偶,且都和部分等价关系没有交集。 PartialOrd中的 <= 和 >= 指的是对应的拟序关系和部分等价关系的并集,<= 和 >=互为对偶。 实际上这些条件是不够的,里面仍然包含了一些非常复杂的情况。 (5)关系之间的传递性 根据实际情况,我认为实际上是有rust建议六:partial_cmp最好满足如下4条传递性: 根据前面的性质和新增的这4条传递性,可以推出<=和>=也具有传递性。 (6)等价自反子集 等价自反子集:由满足a==a的所有的a构成的集合。 例如,对于float集合,去掉NAN得到的就是等价自反子集。 显然,对于一个具有部分等价关系的集合,它的等价自反子集一定具有等价关系。 (7)非自反元素的隔离 rust建议七:如果a!=a,则a和任意元素的partial_cmp都返回None。 例如,f32类型就是满足建议六和建议七的。 (8)等价自反子集的映射子集上的偏序 实际上,即使加上了建议六和建议七,<=仍然不是等价自反子集上的偏序关系。 示例: 这还是一个比较简单的例子,还有很多更复杂的例子都满足上面的所有性质和建议。 一般情况下,在讨论自反性时,我们说a和b是同一个元素,指的是a和b是同一个内存地址中的数据,满足等价关系或者完全相等的数值并不代表是同一个元素。 所以,我们首先把等价自反子集做一个映射,把所有等价的元素映射到一个元素上,得到等价自反子集的映射子集。 这样,我们就可以说,在等价自反子集的映射子集上,PartialOrd中的<=是偏序。 (9)示例 在这个例子中,集合是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特征都要实现,且需要保持逻辑一致(对偶)。 (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个函数都没有默认实现。 (1)基本类型 整数类型都有默认的全序关系,1<=2<=3 浮点数有默认的偏序关系,1<=1.5<=2 其他基本类型同理。 (2)结构体的默认序关系 如果结构体用derive声明默认的PartialOrd或者Ord,则按照字典序推导出序关系。 当然,能编译通过的前提是所有成员都有相应的序关系。 (3)容器的默认序关系 以vec为例,默认序关系就是字典序: 本节内容只讨论同类型数据之间的关系处理原则,不同类型之间没什么要注意的。 (1)PartialEq和Eq 正常情况下,我们都应该实现PartialEq特征,而Eq特征虽然继承了PartialEq但并没有自己的方法需要实现。 (2)偏序的PartialOrd 如果实现一个偏序,则只实现PartialOrd,无论把PartialOrd实现成什么样,都建议Ord是不存在的。 比如,这里我实现了PartialOrd,又声明了默认的Ord,则会造成不一致: (3)全序的PartialOrd和Ord 当我们把PartialOrd实现成去全序时,建议一律采用如下的固定实现方式,即只调用cmp推导出partial_cmp。 例如,全序的二叉堆,虽然源码只会调用<=函数,即只会调用partial_cmp而不会调用cmp,但是二叉堆的数据类型必须是含有Ord特征的类型,f32就编译不过。 因为,有Ord特征就暗示了是全序关系,PartialOrd特征没法说明自身是否是全序,就不能完全满足堆的需求。 所以,最好就是直接实现cmp,然后用cmp推导出partial_cmp。 同理,自定义排序,虽然源码只会调用cmp而不会调用partial_cmp,但最好就是直接实现cmp,然后用cmp推导出partial_cmp。 (4)偏序集的等价自反子集 例如,我要用浮点数作为堆的类型,而且我知道肯定不会有NAN的出现,那么可以这么写:5,std::cmp::Ord
6,默认序关系
7,自定义序关系的场景