0. 所有权机制和借用
所有权机制 一个值只能由一个变量作为所有者 离开作用域即生命周期就会释放 同一时间只能有一个所有者(单一所有权) 借用 只能同时拥有一个可变引用和多个不可变引用
1. 关于Drop
析构 实现:
fn drop<T>(_x: T) {}
_x会在返回前自动丢弃, 编译器不允许显性调用drop,但可以通过Trait mem::drop的方式调用 结构体中优先丢弃_x再依次丢弃_x.one 、 _x.two … 但局部变量会以相反的顺序丢弃
Drop和Copy是排他的
2. Copy 和 Move
let a:T = b;
只有T: Clone + Copy 才会 Copy
T: Clone + !Copy 以及 T: !Clone 为Move
3. HashMap 和 BTreeMap
B-Tree: 自平衡的数,数据有序
HashMap是基于哈希表 BTreeMap是基于BTree BTree是动态分配的,所以不需要指定
4. HashMap和HashSet
hash set,实现为 HashMap,其中值为 ()。
HashSet只有键没有值,是键的集合(无重复)
5. Sized/Send/Sync/Unpin
Sized: 编译时大小已知,编译器会自动为T实现Sized Trait
Send 、 Sync:并发安全
- unsafe auto trait
- Send: T跨线程移动,独占访问是线程安全的
- Sync:&T线程共享,只读共享是安全的
- 不支持的数据结构: const T/mut T 、UnsafeCell 、Rc tips Arc、Rc 区别在于Arc使用原子操作访问,而Rc是内存操作访问 Arc支持多线程,而Rc是单线程
6. Deref/DerefMut
pub trait Deref {
// 解引用出来的结果类型
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
7. Debug、Display
Debug可以派生宏实现通过”“打印
Display必须手动实现通过”{}”打印
8. 切片 slice
切片是描述一组属于同一类型、长度不确定的、在内存中连续存放的数据结构,动态增长
可以查看某集合的一部分数据
9. 迭代器
使用iter等进行调用,使用map、flier等进行处理
只有最后进行next操作才会真正执行 比如collect()
10. 自定义Hash Key
实现Hash、PartialEq、Eq
11. Error Trait
pub trait Error: Debug + Display {
/// 此错误的下级来源 (如果有)
fn source(&self) -> Option<&(dyn Error + 'static)> { ... }
/// 返回一个描述错误的字符串
fn description(&self) -> &str { ... }
/// 指向当前错误造成的原因 这个方法应该返回那个导致当前错误的错误对象。
fn cause(&self) -> Option<&dyn Error> { ... }
fn provide<'a>(&'a self, demand: &mut Demand<'a>) { ... }
}
From GPT
-
source(&self) -> Option<&(dyn Error + ‘static)> 方法用来返回此错误的下级来源(如果有的话),返回类型是一个包含 Error trait 的引用的 Option。
-
description(&self) -> &str 方法返回一个描述错误的字符串。
-
cause(&self) -> Option<&dyn Error> 这是旧版本的方法,现在一般不建议使用。
-
provide<’a>(&’a self, demand: &mut Demand<’a>) 方法接受一个 demand 参数,并且返回一个生命周期绑定到 self 的一个引用。
12. 闭包
如果闭包不使用move语义则借用上下文变量
闭包约束为Send + ‘static的原因
Send 为了线程安全,可以跨线程使用 ‘static 确保不会发送悬垂引用以及生命周期问题
FnOnce 只能调用一次 涉及到捕获并move时会被判断为FnOnce 例如
let c = move |greeting: String| (greeting, name); // FnOnce
let c = move |greeting: String| (greeting, name.clone()); // 非FnOnce
因为 Fn 继承 FnMut , 而 FnMut 继承了 FnOnce FnOnce是Fn FnMut 的super trait
所以当Fn 或者 FnMut 当作 impl FnOnce 传入时,会被转换为FnOnce,因而只能调用一次
Fn 不同于 FnMut
Fn传入的是&self FnMut为&mut self 代表捕获的参数可被改变即mut
13. 泛型
对于结构
struct id<T>{
value: i32,
}
struct User{
id: id<Self>,
}
struct Pro{
id: id<Self>,
}
会编译不通过
因为Struct id并没有使用T而编译器不容许这样的当前未使用在未来可能使用的情况
改为以下就能通过编译
struct id<T>{
value: i32,
_tag: PhantomData<T>,
}
PhantomData幽灵数据,编译器不会实际产生参数,而是用于编译检查提示编译器T会用到
14. 并发
无畏并发
避免
- while 取锁
- 修改左值加锁
在次期间产生的问题,引入原子操作CAS(比较与修改操作, 指令读取某个内存地址,判断其值是否等于某个前置值,如果相等,将其修改为新的值)
while self
.locked
// compare_exchange Rust 提供的 CAS 操作
// 保证读操作对应读false 保证写操作对应赋值为true
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)// 比较是否为false即能够取锁,是则加锁(true)并向下执行反之继续循环
.is_err() {}
//需要的操作执行完后解锁同理
self.locked.store(false, Ordering::Release);
优化
while self
.locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
// 性能优化:compare_exchange 需要独占访问,当拿不到锁时,我们
// 先不停检测 locked 的状态,直到其 unlocked 后,再尝试拿锁
while self.locked.load(Ordering::Relaxed) == true {}
}
15. Condvar 与 Mutex
Mutex 用于保证条件在读写时互斥,Condvar 用于控制线程的等待和唤醒
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let (lock, cvar) = &*pair;
主线程
let mut started = lock.lock().unwrap();
cvar.wait(started).unwrap(); // 堵塞等待started改变并返回改变的值
16. 异步
无畏并发
Future 的调度是协作式多任务 协程
tokio::async 会运行Future await会等待Future执行完毕
tips: Framed::new(stream, LinesCodec::new());
tokio_util::codec::Framed的解码器,将编码以某种方式解码以进行I/O操作
17. 通道 channel
let (tx, rx) = channel::<i32>();
let tx1 = tx.clone();
thread::spaw(move || {
tx.send(1).unwrap();
});
thread::spawn(move || {
tx1.send(2).unwrap();
});
rx.recv().unwrap();