The authoritative guide to Rust in 5 minutes (39) unsafe

The authoritative guide to Rust in 5 minutes (39) unsafe

Unsafe Rust

Because of the inherent insecurity of the underlying computer hardware. If rust does not allow unsafe operations, then some underlying tasks may not be completed at all. Rust has "insecure superpowers" to operate with the operating system. I understand that rust itself has a higher degree of freedom than C, C++, etc. The language has an extra layer of security encapsulation, but security is conditionally restricted, and many functions cannot be realized. At this time, you need to get rid of this layer of restriction and use "unsafe rust" to realize it.

Dereference raw pointer

There are two new pointer types that are similar to references in the unsafe Rust world. They are both called raw pointers (or raw pointers in some places). Similar to references, raw pointers are also divided into two types:

//immutable raw pointer * const T @ raw pointer variable * MUT T duplicated code

Note that the asterisk at the beginning of the raw pointer is part of the type name and not a dereference operation

The difference between raw pointers and references and smart pointers:

  • It is allowed to ignore the borrowing rules, and you can have both variable and immutable pointers to the same memory address, or multiple variable pointers to the same address.
  • There is no guarantee that you always point to a valid memory address.
  • Allow to be empty.
  • No automatic cleanup mechanism is implemented.

Create immutable raw pointers

You can use type declaration and type conversion to create raw pointers:

let num = 5 ; //Type declaration immutable raw pointer let r1: * const i32 = # //Use the as operator to convert the reference to a raw pointer: let r2 = &num as * const i32 ; ! the println ( "R1 Memory Address: {:?}" , R1); //R1 Memory Address: 0x7ffee6b7323c ! the println ( "memory address R2: {:?}" , R2); //R2 Memory Address: 0x7ffee6b7323c copy the code


r1'' and
All passed
num``` is created, so the memory address is the same.

Create a mutable raw pointer

The mutable raw pointer is similar to the immutable raw pointer above, except that

Replace with

let r1: * mut i32 = & mut num; //Equivalent: let r2 = & mut num as * mut i32 ; ! the println ( "R1 Memory Address: {:?}" , R1); //R1 Memory Address: 0x7ffee6b7323c ! the println ( "memory address R2: {:?}" , R2); //R2 Memory Address: 0x7ffee6b7323c copy the code

Create a raw pointer to an arbitrary memory address

We can directly convert a number to a bare pointer:

//0x12345 is a hexadecimal number, of course it can also be written as a decimal 74565 let address = 0x12345usize ; let r1 = &address as * const usize ; println! ( "r1 memory address{:?}" , r1); //r1 memory address : 0x7ffee6b73380 //Of course, it is also possible to use variable references and bare references. //The decimal i32 type is used here. I just want to show that any number type can be used let mut address2 = 74565i32 ; let r2 = & mut address2 as * mut i32 ; println! ( "r2 memory address {:?}" , r2); //r2 memory address: 0x7ffee6b73380 copy the code

Raw pointer dereference

Raw pointers support dereference, which is used to obtain the real data corresponding to the pointer, but it needs to be used


let mut num = 5 ; let r = &num as * const i32 ; //View the address println! ( "{:?}" , r); //0x7ffee6b733dc //Dereference to get data println! ( "{}" , *r); //Report an error, you need to use an unsafe block to dereference raw pointers //Dereference the bare pointer in the unsafe block unsafe { println! ( "{:?}" , *r); //5 } Copy code

Call unsafe functions or methods

The unsafe function definition also needs to be added in front

The keyword means that there are unsafe operations in the function:

unsafe fn dangerous () -> i32 { let mut num = 5 ; let r = &num as * const i32 ; return *r } dangerous(); //Report an error, it is not allowed to call unsafe functions outside the unsafe block //call unsafe in an unsafe block { let num = dangerous(); println! ( "{}" , num); //5 } Copy code

Create a safe abstraction for unsafe code

The inclusion of unsafe code in a function does not mean that we need to mark the entire function as unsafe. In fact, encapsulating unsafe code in a safe function is a very common abstraction. For example, the following uses safe


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 ]); copy Code

Used in the above code

The function divides an array reference into two variable references of the array according to the index. If we are in a safe rust, we cannot implement this function:

fn split_at_mut (slice: & mut [ i32 ], index: usize ) -> (& mut [ i32 ], & mut [ i32 ]) { let a = & mut slice[..index]; let b = & mut slice[ index..];//Report an error, you cannot use variable references to borrow slices multiple times return (a, b) } Copy code

In the above code, because rust does not allow us to create two variable references to a piece of data at the same time, an error is reported. At this time, we can use

Block realization:

fn split_at_mut (slice: & mut [ i32 ], index: usize ) -> (& mut [ i32 ], & mut [ i32 ]) { //Slice reference is composed of start position and length //Get the length of the slice let len = slice.len(); //Get the raw pointer of the starting position of the slice let ptr = slice.as_mut_ptr(); //Make sure that the slice position cannot be greater than the slice length assert! (index <= len); //The starting position address of slice in memory println! ( "Memory address: {:?}" , ptr); //Memory address: 0x7ff765c05b20 unsafe { use std::slice; //Dereference the starting position of the slice to get the first element println! ( "First element: {}" , *ptr); //First element: 6 //The first fragment, through the bare pointer position, directly obtains a slice of the specified length in the memory let a = slice::from_raw_parts_mut(ptr, index); println! ( "a: {:?}" , a); //a: [6, 5, 4] //At the beginning of the second fragment, the memory address pointed to by the bare pointer needs to be moved backward by index, that is: the length of the first fragment let move_to_index = ptr.offset(index as isize ); //Get the remaining length of the complete slice through len-index as the length of the second slice let b = slice::from_raw_parts_mut(move_to_index, len-index); println! ( "b: {:?}" , b); //b: [3, 2, 1] (a, b) } } let mut v = vec! [ 6 , 5 , 4 , 3 , 2 , 1 ]; let r = & mut v[..]; let (a, b) = split_at_mut(r, 3 ); assert_eq! (a, & MUT [ . 6 , . 5 , . 4 ]); ASSERT_EQ! (B, & MUT [ . 3 , 2 , . 1 ]); copy Code

The above code does not change

The function is marked as
, So you can call this function in Safe Rust. We created a safe abstraction of unsafe code and used it in a safe way when implementing it
Code, because it only creates a valid pointer to access the data, but sometimes
The function may cause a crash:

use std::slice; let address = 0x12345usize ; let ptr = address as * const i32 ; unsafe { let data: &[ i32 ] = slice::from_raw_parts(ptr, 10000 as usize ); //error, this section cannot be guaranteed The code slice always contains a valid i32 value println! ( "data: {:?}" , data); } Copy code

Because we only own the memory address and not the memory data, there is no guarantee that other variables will use this memory, and then modify the value inside, resulting in the value inside being not valid

Type, so the compilation failed.

Use extern function to call external code

In addition, in order to call each other with other languages, rust specifically provides

Keywords to simplify the process of creating and using a Foreign Function Interface (FFI). FFI is a way of programming languages to define functions, which allows other (external) programming languages to call these functions:

//Declare the external external function signature extern "C" { fn abs (input: i32 ) -> i32 ; } //Need to call unsafe in the unsafe module { abs(- 10 ); } // Expose methods to other languages //for annotations to prevent Rust from changing its name at compile time #[no_mangle] pub extern "C" fn call_from_c () { println! ( "C language calls this method" ); } Copy code

Access or modify a variable static variable

In rust, global variables are also called static (

) Variable, define and use an immutable static variable:

static hello_world: & STR = "Hello World" ; the println! ( "{}" , hello_world); //Hello World duplicated code

Constants and immutable static variables may look very similar, but there is a very subtle difference between them: The value of a static variable has a fixed address in memory, and using its value will always access the same data. In contrast, constants allow their data to be copied whenever they are used.

Static variables are allowed to be variable

Static variables can also be used

To mark it as variable, but when you modify it, you can only
In the e block:

static mut COUNTER: u32 = 0 ; //COUNTER = 1;//An error is reported, and an unsafe block is required for use or modification unsafe { COUNTER = 1 ; println! ( "{}" , COUNTER); //1 } Copy code

Implement unsafe traits

and also

, When a
When there is at least one method in which the compiler cannot verify the unsafe factors, we call this
Is insecure, the same needs to be used
To identify this

//Define and implement an unsafe trait unsafe trait Foo { fn foo () {} } //Implement Foo trait unsafe impl Foo for i32 { fn foo () {} } Copy code

Cover image: Follow Tina to draw America

Pay attention to the official account of "Ma Sheng Bi Tan" to read more latest chapters