美文网首页
【RUST_BASIC】不安全 Rust

【RUST_BASIC】不安全 Rust

作者: ixiaolong | 来源:发表于2021-11-26 11:22 被阅读0次

1 简介

Rust 在编译时会强制执行的内存安全保证,但是 Rust 中还存不强制执行内存安全保证的不安全 Rust(unsafe Rust),它与常规 Rust 代码无异,但是会提供额外的超能力。

静态分析本质上是保守的,当编译器尝试确定一段代码是否支持某个保证时,拒绝一些合法的程序比接受错误的程序要好一些。这必然意味着有时代码可能是合法的,但如果 Rust 编译器没有足够的信息来确定,它将拒绝该代码。在这种情况下,可以使用不安全代码。

另一个 Rust 存在不安全一面的原因是:底层计算机硬件固有的不安全性。如果 Rust 不允许进行不安全操作,那么有些任务则根本完成不了。Rust 需要能够进行像直接与操作系统交互,甚至于编写操作系统这样的底层系统编程。

可以通过 unsafe 关键字来切换到不安全 Rust,接着可以开启一个新的存放不安全代码的块。这里有五类可以在不安全 Rust 中进行而不能用于安全 Rust 的操作:

  • 解引用裸指针
  • 调用不安全的函数或方法
  • 访问或修改可变静态变量
  • 实现不安全 trait
  • 访问 union 的字段
    unsafe 关键字只是提供了五个不会被编译器检查内存安全的功能,不会关闭借用检查器或禁用任何其他 Rust 安全检查。

2 解引用裸指针

不安全 Rust 有两个被称为裸指针(raw pointers)的类似于引用的新类型,分为不可变或可变的,分别写作 *const T*mut T,裸指针与引用和智能指针的区别在于:

  • 允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针
  • 不保证指向有效的内存
  • 允许为空
  • 不能实现任何自动清理功能
let mut num = 5;

let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;

unsafe {
    println!("r1 is: {}", *r1);
    println!("r2 is: {}", *r2);
}

3 extern 调用外部代码

有 Rust 可能需要与其他语言编写的代码交互,为此 Rust 有一个关键字 extern,有助于创建和使用外部函数接口(Foreign Function Interface, FFI)。外部函数接口是一个编程语言用以定义函数的方式,其允许不同(外部)编程语言调用这些函数。

extern 块中声明的函数在 Rust 代码中总是不安全的。因为其他语言不会强制执行 Rust 的规则且 Rust 无法检查它们,所以确保其安全是程序员的责任:

extern "C" {
    fn abs(input: i32) -> i32;
}

fn main() {
    unsafe {
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

extern "C" 块中,列出了希望能够调用的另一个语言中的外部函数的签名和名称。"C" 部分定义了外部函数所使用的应用二进制接口(application binary interface,ABI) —— ABI 定义了如何在汇编语言层面调用此函数。"C" ABI 是最常见的,并遵循 C 编程语言的 ABI。

extern 的使用无需 unsafe

4 访问或修改可变静态变量

全局变量在 Rust 中被称为静态(static)变量。

static HELLO_WORLD: &str = "Hello, world!";

fn main() {
    println!("name is: {}", HELLO_WORLD);
}

常量与不可变静态变量可能看起来很类似,区别是静态变量中的值有一个固定的内存地址,使用这个值总是会访问相同的地址,常量则允许在任何被用到的时候复制其数据;常量与静态变量的另一个区别在于静态变量可以是可变的,访问和修改可变静态变量都是不安全的:

static mut COUNTER: u32 = 0;

fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}

fn main() {
    add_to_count(3);

    unsafe {
        println!("COUNTER: {}", COUNTER);
    }
}

5 不安全 trait

当 trait 中至少有一个方法中包含编译器无法验证的不变式(invariant)时 trait 是不安全的。可以在 trait 之前增加 unsafe 关键字将 trait 声明为 unsafe,同时 trait 的实现也必须标记为 unsafe

unsafe trait Foo {
    // methods go here
}

unsafe impl Foo for i32 {
    // method implementations go here
}

6 访问联合体字段

联合体 unionstruct 类似,但是在一个实例中同时只能使用一个声明的字段。联合体主要用于和 C 代码中的联合体交互。访问联合体的字段是不安全的,因为 Rust 无法保证当前存储在联合体实例中数据的类型。

相关文章

网友评论

      本文标题:【RUST_BASIC】不安全 Rust

      本文链接:https://www.haomeiwen.com/subject/fnbtxrtx.html