MST

星途 面试题库

面试题:Rust中unsafe block的常见使用场景有哪些

请简要阐述Rust语言里unsafe block在编程实践中比较常见的几个使用场景,并举例说明。
41.7万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

1. 调用外部函数(FFI)

  • 场景阐述:当需要与其他语言(如C语言)编写的库进行交互时,Rust提供了 extern 关键字来声明外部函数。这些函数通常需要在 unsafe 块中调用,因为Rust无法对外部函数进行安全检查。
  • 示例
// 假设存在一个C函数定义在libexample.so中
// int add(int a, int b);
extern "C" {
    fn add(a: i32, b: i32) -> i32;
}

fn main() {
    unsafe {
        let result = add(2, 3);
        println!("The result of add is: {}", result);
    }
}

2. 操作裸指针

  • 场景阐述:裸指针(*const T*mut T)绕过了Rust的内存安全检查机制。在某些性能敏感或者与底层硬件交互的场景中,可能需要使用裸指针,这时就需要 unsafe 块。
  • 示例
fn main() {
    let mut num = 42;
    let raw_ptr: *mut i32 = &mut num as *mut i32;
    unsafe {
        // 通过裸指针修改值
        *raw_ptr = 43;
        println!("The value is: {}", num);
    }
}

3. 访问和修改未初始化内存

  • 场景阐述:Rust通常要求变量在使用前必须初始化。但在一些特定的底层场景,比如实现内存分配器时,可能需要先分配未初始化的内存,然后再对其进行初始化,这个过程需要在 unsafe 块中完成。
  • 示例
use std::mem;

fn main() {
    let mut uninit: mem::MaybeUninit<i32> = mem::MaybeUninit::uninit();
    unsafe {
        // 将值写入未初始化内存
        uninit.as_mut_ptr().write(42);
        let value = uninit.assume_init();
        println!("The value is: {}", value);
    }
}

4. 实现自定义Drop逻辑

  • 场景阐述:虽然大多数情况下,Rust自动生成的 Drop 实现能满足需求,但在一些复杂场景,比如资源释放依赖特定顺序或者涉及外部资源时,手动实现 Drop 并在其中使用 unsafe 操作可能是必要的。
  • 示例
struct MyResource {
    ptr: *mut u8,
}

impl Drop for MyResource {
    fn drop(&mut self) {
        unsafe {
            std::ptr::drop_in_place(self.ptr);
            std::alloc::dealloc(self.ptr, std::alloc::Layout::new::<u8>());
        }
    }
}