..

Rust - unsafe 特性

上篇内容1unsafe 这个关键字,其实官方文档2写的很清楚了,这里对文档进行一个简单翻译,总结一下知识点。


背景

文档开头说明了一下 unsafe 为什么存在:
1、摆脱 compiler 的束缚。
在一些情况下,compiler 会 reject 正确的代码,如果使用 unsafe 则交给程序员自己处理内存安全。
2、操作硬件
因为硬件是 unsafe 的,放开 unsafe alter ego 是为了更好的操作底层系统。

时机

在什么时候需要用 unsafe code:
1、Dereference a raw pointer
2、Call an unsafe function or method
3、Access or modify a mutable static variable
4、Implement an unsafe trait
5、Access fields of unions

就是遇到以上五个 unsafe superpower actions 的时候是可以使用,方式就是:

unsafe {
	//unsafe codes
}

需要注意的地方就是:compiler 依然会对 unsafe 代码进行 borrow checker 或其他 safety check,unsafe 只是表示 compiler 不进行内存安全性检查。

几点Tips :

1、Keep unsafe blocks small
2、To isolate unsafe code as much as possible
3、 enclose unsafe code within a safe abstraction and provide a safe API


五个 unsafe superpower

Dereferencing a Raw Pointer

使用场景

为什么要使用 Raw Pointer?
1、和 C code 交互
2、处理 compiler 不认识的 safe 代码

Raw Pointer 的形式
* const T 
* mut T

创建

let mut num = 5;

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

1、The asterisk isn’t the dereference operator; it’s part of the type name.
2、In the context of raw pointers, immutable means that the pointer can’t be directly assigned to after being dereferenced.

Different from references and smart pointers, raw pointers:

1、Are allowed to ignore the borrowing rules by having both immutable and mutable pointers or multiple mutable pointers to the same location Aren’t guaranteed to point to valid memory
2、Are allowed to be null
3、Don’t implement any automatic cleanup

Tips

1、we can create raw pointers in safe code
2、we can’t dereference raw pointers and read the data being pointed to
3、With raw pointers, we can create a mutable pointer and an immutable pointer to the same location and change data through the mutable pointer
4、data race


Calling an Unsafe Function or Method

方式
unsafe fn dangerous() {}

unsafe {
    dangerous();
}
特性

1、Unsafe functions and methods look exactly like regular functions and methods
2、they have an extra unsafe before the rest of the definition
3、By calling an unsafe function within an unsafe block
4、Bodies of unsafe functions are effectively unsafe blocks

Creating a Safe Abstraction over Unsafe Code

Just because a function contains unsafe code doesn’t mean we need to mark the entire function as unsafe. In fact, wrapping unsafe code in a safe function is a common abstraction.

以split_at_mut解释
    let mut v = vec![1, 2, 3, 4, 5, 6];

    let r = &mut v[..];

    let (a, b) = r.split_at_mut(3);

    assert_eq!(a, &mut [1, 2, 3]);
    assert_eq!(b, &mut [4, 5, 6]);

Rust’s borrow checker can’t understand that we’re borrowing different parts of the slice; it only knows that we’re borrowing from the same slice twice. Borrowing different parts of a slice is fundamentally okay because the two slices aren’t overlapping, but Rust isn’t smart enough to know this. When we know code is okay, but Rust doesn’t, it’s time to reach for unsafe code.

Using extern Functions to Call External Code
extern "C" {
    fn abs(input: i32) -> i32;
}

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

1、Rust code might need to interact with code written in another language 2、the ABI defines how to call the function at the assembly level.


Accessing or Modifying a Mutable Static Variable

1、In Rust, global variables are called static variables.
2、Static variables can only store references with the ‘static lifetime
3、Accessing an immutable static variable is safe.
4、static variable have a fixed address in memory.
5、static variables can be mutable

static mut COUNTER: u32 = 0;

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

fn main() {
    add_to_count(3);

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

Implementing an Unsafe Trait

1、A trait is unsafe when at least one of its methods has some invariant that the compiler can’t verify.
2、by adding the unsafe keyword before trait
3、marking the implementation of the trait as unsafe

unsafe trait Foo {
    // methods go here
}

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

fn main() {}
  • 以使用 Sync and Send 为例3

Accessing Fields of a Union

1、A union is similar to a struct, but only one declared field is used in a particular instance at one time.
2、Unions are primarily used to interface with unions in C code

这部分也是跟C code打交道时使用的多。


结论

以上就是使用 unsafe 的五个场景。

可以说,Rust 既想获得遗产(C code),又有野心(borrow checker, safety check…),于是造出了 unsafe 这个 alter ego。