跳过正文

不安全:unsafe

··824 字
Rust
rust-lang - 这篇文章属于一个选集。
§ 20: 本文

通过使用 unsafe 关键字,可以将 Rust 安全代码和非安全代码建立边界。一般来说 unsafe 代码(fun/block/trait)都有使用前提,使用方需要保证满足这些约束,防止出现未定义的行为。

尽量使用安全代码,这样 Rust 编译器、type checker、borrow checker 和其它静态 checker 会详细检查代码的合法性。unsafe 代码不会有这些安全性优势,需要开发者自己来保证。常见的做法是:将 unsafe block 封装在安全的函数中。

unsafe {} 用于指示编译器忽略一些严格的安全检查(但不忽略所有检查), 主要使用场景:

  1. 读写裸指针(安全代码可以创建、转移、比较 raw pointer,但是不能解引用和赋值 raw pointer);
  2. 调用外部库函数(FFI 函数);
  3. 调用 unsafe 函数或方法;
  4. 读写 static mut 全局变量;
  5. 实现 unsafe trait;
// from_utf8_unchecked 是 unsaft 函数,只能在 unsafe 上下文中调用
unsafe {
    String::from_utf8_unchecked(ascii)
}

// 调用外部函数接口
extern "C" {
    fn abs(input: i32) -> i32;
}
fn main() {
    unsafe {
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

// 读写 static mut 变量
static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}
fn main() {
    add_to_count(3);
    unsafe {
        println!("COUNTER: {}", COUNTER);
    }
}

在 unsafe block 中读写裸指针:

fn main() {
    let mut a: usize = 0;
    let ptr = &mut a as *mut usize;
    unsafe {
        *ptr.offset(3) = 0x7ffff72f484c;
    }
}
// $ cargo build
//      Compiling unsafe-samples v0.1.0
//       Finished debug [unoptimized + debuginfo] target(s) in 0.44s
// $ ../../target/debug/crash
//     crash: Error: .netrc file is readable by others.
//     crash: Remove password or make file unreadable by others. Segmentation fault (core dumped)

unsafe function 是添加了 unsafe 标识的函数,它的 body 对应是 unsafe block。

  • unsafe 和普通函数完全一样,只是用 unsafe 标记后,表示调用该函数需要满足一些先决条件,需要开发者来保证。
  • 只能在 unsafe function/block 中调用 unsafe 函数。
pub unsafe fn from_bytes_unchecked(bytes: Vec<u8>) -> Ascii {
    Ascii(bytes)
}

// 使用 unsafe 特性实现的安全函数
use std::slice;
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    let ptr = slice.as_mut_ptr();
    assert!(mid <= len);
    unsafe { // 需要在 unsafe block 中调用 unsafe 函数
        (slice::from_raw_parts_mut(ptr, mid),
       slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
    }
}

use std::slice;
let address = 0x01234usize;
let r = address as *mut i32;
let slice : &[i32] = unsafe {
    slice::from_raw_parts_mut(r, 10000)
};

unsafe trait 是添加了 unsafe 标识的 trait,在 impl 它时,也必须添加 unsafe 标识:

  • unsafe trait 和普通 trait 完全一样,只是表明该 trait 的定义或实现有一些前提约束,Rust 编译器无法检查,需要开发者来保证。
// 定义 unsafe trait
pub unsafe trait Zeroable {}

// 实现 unsafe trait
unsafe impl Zeroable for u8 {}
unsafe impl Zeroable for i32 {}
unsafe impl Zeroable for usize {}
// and so on for all the integer types

// 使用 unsafe trait,不一定需要 unsafe 函数
use core::nonzero::Zeroable;
fn zeroed_vector<T>(len: usize) -> Vec<T> where T: Zeroable
{
    let mut vec = Vec::with_capacity(len);
    unsafe {
        std::ptr::write_bytes(vec.as_mut_ptr(), 0, len);
        vec.set_len(len);
    }
    vec
}
rust-lang - 这篇文章属于一个选集。
§ 20: 本文

相关文章

借用:refer/borrow
··3120 字
Rust
Rust 引用类型和借用
内置类型:type
··16432 字
Rust
Rust 内置基本类型介绍
函数、方法和闭包:function/method/closure
··6964 字
Rust
Rust 函数、方法和闭包
包和模块:package/crate/module
··1958 字
Rust
Rust 项目的包和模块组织结构