Rust基础知识回顾

·2594·6 分钟·
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的生命周期