Rust- unsafe

raw pointer

Raw pointers in Rust are similar to pointers in C. They allow for manual, direct manipulation of memory. There are two types of raw pointers in Rust:

  • *const T, which is an immutable raw pointer (you can’t modify the data it points to).
  • *mut T, which is a mutable raw pointer (you can modify the data it points to).

Here’s an example of raw pointers:

let mut x = 5;
let raw = &mut x as *mut i32;

let points_at = unsafe { *raw };
println!("raw points at {}", points_at);

In the above example, raw is a mutable raw pointer that points to the memory location of x. unsafe is required to dereference raw pointers (with *raw), because the compiler cannot guarantee that the memory location is valid, not freed, not null, and properly aligned.

Remember, it’s very rare that you’ll need to use raw pointers in everyday Rust programming. The majority of the time, Rust’s safe pointers (references) are more than sufficient. Raw pointers are used when you need to interface with foreign code (like C libraries), when you need to share memory between threads (concurrency), or when doing complex memory manipulations (like writing your own data structures).

unsafe

The unsafe keyword in Rust indicates that the programmer explicitly knows what they’re doing, understands the potential risks involved, and accepts any consequences. In unsafe blocks, Rust relaxes some of its guarantees, allowing the programmer to perform actions that might be allowed in other languages like C++. Here are some of the actions that can be performed within an unsafe block:

  1. Dereference raw pointers: Rust only allows the use of safe references in normal code, which are non-null and guaranteed not to cause data races. However, unsafe in Rust allows you to create and dereference raw pointers.

  2. Call unsafe functions or methods: Some Rust functions or methods are marked as unsafe, indicating they do something the compiler can’t verify as safe. To call these functions or methods, you need to do so within an unsafe block.

  3. Access or modify mutable static variables: Rust’s static variables are similar to global variables in other languages. Accessing or modifying mutable static variables needs to be done within an unsafe block.

  4. Implement unsafe traits: Some traits are marked as unsafe, indicating that any type implementing this trait needs to uphold certain invariants. Implementing these traits needs to be done within an unsafe block.

The unsafe keyword should be used with caution. Although unsafe allows you to bypass some of Rust’s checks, improper use can lead to memory safety issues or data races. In most cases, you should try to avoid using unsafe, and only use it when absolutely necessary. Furthermore, you should try to encapsulate unsafe code within safe APIs to minimize the potential damage of unsafe code.

And here are the examples for each scenario:

  1. Dereferencing a raw pointer:
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);
}

In the code above, we first create two raw pointers r1 and r2, and then dereference them in an unsafe block. Note that although creating raw pointers is safe, dereferencing raw pointers is unsafe.

  1. Calling an unsafe function or method:
unsafe fn dangerous() {}

unsafe {
    dangerous();
}

In the code above, we define an unsafe function dangerous and then call it in an unsafe block.

  1. Accessing or modifying mutable static variables:
static mut COUNTER: u32 = 0;

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

In the code above, we define a mutable static variable COUNTER and then modify it in a function with an unsafe block.

  1. Implementing unsafe traits:
unsafe trait Foo {}

unsafe impl Foo for i32 {}

Note: In Rust, unsafe impl Foo for i32 {} means that you are providing an implementation of an unsafe trait named Foo for the type i32.

Traits in Rust are a way to define behavior that types should have. They are similar to interfaces in languages like Java. An “implementation” of a trait for a type, or impl in short, is where you provide the actual code for that behavior.

When you see unsafe before impl, it means that the trait being implemented (Foo in this case) is marked as unsafe. This signifies that implementing the trait involves some invariant (a condition always true in correct usage) that the compiler can’t check.

For example, suppose we have the following unsafe trait:

unsafe trait UnsafeTrait {
    fn dangerous_operation(&self);
}

This trait might represent some operations that are potentially unsafe and can’t be checked by the compiler. To implement this trait for a type, we use unsafe impl:

unsafe impl UnsafeTrait for i32 {
    fn dangerous_operation(&self) {
        // some potentially unsafe operation here
    }
}

In this example, we’re promising that our implementation of dangerous_operation for i32 adheres to the requirements that UnsafeTrait specifies (but which the compiler can’t verify).

As always, the unsafe keyword in Rust is a signal that extra care needs to be taken. It’s a way of saying “I, the programmer, have checked this carefully and it is correct, even though the compiler can’t check it for me.”

These are just some basic examples of unsafe usage. The scope of unsafe is far more extensive, and it can be used for tasks like building and interfacing with FFI (Foreign Function Interface). However, bear in mind that while unsafe lets you bypass some of Rust’s checks, misuse can lead to memory safety issues or data races. In most cases, you should try to avoid using unsafe, and only use it when absolutely necessary.

你可能感兴趣的:(Rust,rust)