Rust基础知识回顾
AI摘要: 本文系统回顾了Rust语言的核心机制,包括所有权转移(通过赋值和into实现)、单线程共享的Rc智能指针、跨线程安全的Arc智能指针、互斥锁Mutex用于多线程数据保护、借用辅助函数as_ref/as_deref进行安全引用转换,以及Option/Result的错误处理模式和生命周期注解规则。重点解析了这些特性在内存管理、并发编程中的实现原理与应用场景。
rust的语法有些过于复杂,所以有的时候还是需要简单回顾下。
所有权转移
赋值号=
let a = 6;
let b = a; // 直接发生了所有权转移
into
这是一个语法糖,需要配合From trait
使用,主要作用是所有权转移 + 类型转换
let a : String = "hello".to_string();
let t: Box<str> = s.into() // 发生所有权转移,并将类型转化为Box<str>
所有权共享
虽然独享很安全,但是很多时候需要多个变量共享所有权,这里面又涉及到多线程和单线程两种不同情况
单线程共享 Rc<T>
多个所有者,共享一个东西,本质是引用计数
let a = Rc::new(5)
let b = Rc::clone(&a);
println!("{}", a + b);
栈内存:
+-----+ Rc<T> (8字节左右)
| a | -----> ptr --------------------+
+-----+ |
v
堆内存:
+---------------------------------------+
| RcBox<T>: |
| strong = 1 |
| weak = 0 |
| value = 42 |
+---------------------------------------+
栈内存:
+-----+ Rc Rc
| a | -----> [ ptr ] ------+ [ ptr ] <----- | b |
+-----+ | +-----+
v
堆内存 (RcBox<T>):
+-------------------+
| strong = 2 | <- 引用计数 +1
| weak = 0 |
| value = 42 |
+-------------------+
多线程共享Arc<T>
和Rc
类似,但是跨线程安全
let a= Arc::new(5);
let b = Arc::clone(&a);
let t = thread::spawn(move || {
println!("{}", b);
})
t.join().unwrap();
互斥锁Mutex<T>
多线程共享数据时候,修改数据需要上锁
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10:
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num +=1;
}));
for h in handles{
h.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
借用辅助:as_ref
, as_deref
as_ref: 只借用,不要所有权
let x: Option<String> = Some("hi".to_string());
let y: Option<&String> = x.as_ref(); // y是借用
as_deref: 把Option<Box>转化为Option<&T>, 自动解引用
let x: Option<Box<String>> = Some(Box::new("hi".to_string()));
let y: Option<&str> = x.as_deref().map(|s| s.as_str());
各种解构和错误处理的语法糖
Option
常见方法
let x: Option<i32> = Some(5);
x.is_some(); // True
x.is_none(); // False
x.unwrap(); // 5
x.unwrap_or(10) // 5, 如果是None,就返回默认值10
x.map(|v| v* 2) // Some(10)
Result
从常见方法
let r: Result<i32, &str> = Err("error");
r.is_ok() // false
r.is_err() // true
r.unwrap_or(10) // 10
r.map_err(|e| e.to_uppercase()) // Err("ERROR")
生命周期
告诉编译器“引用要活多久”:返回引用的时候,必须声明生命周期,否则编译器不知道返回值要活多久
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() { s1 } else { s2 }
}
pub struct BangumiParser<'a> {
client: &'a BiliCLient
}
impl<'a> BangumiParser<'a> {
// ...
}
- 其中
<'a>
表示BangumiParser的生命周期,而client的引用的生命周期是&'a
- 这里就是限制,client的引用的生命周期,必须要大于等于BangumiParser的生命周期