面试题答案
一键面试主要区别
- 所有权语义:
&
引用:不拥有数据,只是借用数据。多个&
引用可以同时指向同一个数据,但是在同一作用域内不能同时存在可变&mut
引用和不可变&
引用(除了&mut
引用单独存在的情况),以确保数据一致性。Rc<T>
:通过引用计数拥有数据的部分所有权。多个Rc<T>
实例可以指向同一个数据,当最后一个Rc<T>
实例被销毁时,其所指向的数据也会被销毁。
- 线程安全性:
&
引用:本身不是线程安全的。在并发场景下,如果多个线程同时访问和修改通过&
引用指向的数据,会导致数据竞争,除非使用线程同步机制(如Mutex
等)来保护数据。Rc<T>
:同样不是线程安全的。Rc<T>
主要用于单线程环境,因为其引用计数的修改不是原子操作,在多线程环境下使用会导致未定义行为。
适用的并发编程需求
&
引用:- 适用于在单线程内部,需要在不同函数或代码块之间传递数据的只读或短暂写访问场景。例如,在一个函数内部,将数据传递给另一个函数进行只读操作:
fn print_number(num: &i32) {
println!("The number is: {}", num);
}
fn main() {
let my_num = 42;
print_number(&my_num);
}
Rc<T>
:- 适用于单线程环境下,需要在多个对象之间共享数据,且数据不会被修改(或者使用内部可变性机制如
Cell
或RefCell
来实现安全的修改)的场景。例如,在一个单线程的图形渲染系统中,多个图形对象可能共享同一个材质数据:
- 适用于单线程环境下,需要在多个对象之间共享数据,且数据不会被修改(或者使用内部可变性机制如
use std::rc::Rc;
struct Material {
name: String,
// other properties
}
struct Shape {
material: Rc<Material>,
// other shape - related properties
}
fn main() {
let shared_material = Rc::new(Material {
name: "plastic".to_string(),
// initialize other properties
});
let shape1 = Shape {
material: Rc::clone(&shared_material),
// initialize other shape - related properties
};
let shape2 = Shape {
material: Rc::clone(&shared_material),
// initialize other shape - related properties
};
}
在并发场景下,如果需要共享可修改的数据,Rc<T>
配合Mutex
等同步原语可以实现线程安全的共享,但更推荐使用线程安全的Arc<T>
类型。例如:
use std::sync::{Arc, Mutex};
fn main() {
let shared_data = Arc::new(Mutex::new(0));
let data_clone = Arc::clone(&shared_data);
std::thread::spawn(move || {
let mut data = data_clone.lock().unwrap();
*data += 1;
}).join().unwrap();
let mut data = shared_data.lock().unwrap();
println!("Data: {}", *data);
}
这里Arc<T>
类似Rc<T>
,但可以安全地在多线程间传递,Mutex
用于保护数据的并发访问。