MST

星途 面试题库

面试题:Rust引用在并发编程中的基础应用

在Rust并发编程场景下,简述`&`引用和`Rc`(`Rc<T>`)引用计数类型在使用上的主要区别,以及各自适用于什么样的并发编程需求。请举例说明。
47.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

主要区别

  1. 所有权语义
    • &引用:不拥有数据,只是借用数据。多个&引用可以同时指向同一个数据,但是在同一作用域内不能同时存在可变&mut引用和不可变&引用(除了&mut引用单独存在的情况),以确保数据一致性。
    • Rc<T>:通过引用计数拥有数据的部分所有权。多个Rc<T>实例可以指向同一个数据,当最后一个Rc<T>实例被销毁时,其所指向的数据也会被销毁。
  2. 线程安全性
    • &引用:本身不是线程安全的。在并发场景下,如果多个线程同时访问和修改通过&引用指向的数据,会导致数据竞争,除非使用线程同步机制(如Mutex等)来保护数据。
    • Rc<T>:同样不是线程安全的。Rc<T>主要用于单线程环境,因为其引用计数的修改不是原子操作,在多线程环境下使用会导致未定义行为。

适用的并发编程需求

  1. &引用
    • 适用于在单线程内部,需要在不同函数或代码块之间传递数据的只读或短暂写访问场景。例如,在一个函数内部,将数据传递给另一个函数进行只读操作:
fn print_number(num: &i32) {
    println!("The number is: {}", num);
}

fn main() {
    let my_num = 42;
    print_number(&my_num);
}
  1. Rc<T>
    • 适用于单线程环境下,需要在多个对象之间共享数据,且数据不会被修改(或者使用内部可变性机制如CellRefCell来实现安全的修改)的场景。例如,在一个单线程的图形渲染系统中,多个图形对象可能共享同一个材质数据:
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用于保护数据的并发访问。