What is unsafe keyword in Rust?

In Rust, a distinct part of the language is known as "unsafe Rust," which allows developers to bypass the memory safety guarantees enforced by the compiler. The unsafe keyword indicates code containing operations or invariants that cannot be fully verified at compile-time. The primary reasons for having unsafe Rust are as follows:

  • Conservative static analysis: Rust's compiler is conservative regarding safety checks. It may reject valid code to ensure memory safety. In such cases, unsafe allows developers to inform the compiler that they will manually handle safety concerns.

  • Interfacing with unsafe code: Rust allows developers to interface with low-level codebases, like C or C++ libraries, which may not provide the same safety guarantees as Rust. The unsafe code is used when dealing with raw pointers, mutable static variables, and other low-level operations.

  • Performance optimization: In some situations, unsafe code can improve performance by avoiding certain safety checks. However, this should be done carefully and only after thorough analysis.

Features of the unsafe keyword

The unsafe keyword provides five major features:

  1. Dereferencing raw pointers: Raw pointers (*const T and *mut T) can bypass borrowing rules and aren't guaranteed valid.

  2. Calling unsafe functions or methods: Unsafe functions or methods can be called from unsafe blocks, indicating that the programmer takes responsibility for upholding their contracts.

  3. Accessing or modifying mutable static variables: It is inherently unsafe, and it must be done within an unsafe block.

  4. Implementing an unsafe trait: A trait can be marked as unsafe, indicating that the compiler cannot verify certain invariants, and the implementor must ensure safety.

  5. Accessing fields of a union: Unions are similar to structs but can hold only one field at a time. Accessing fields within a union is considered unsafe due to the compiler's inability to ensure the data type contained in the union.

It's essential to grasp that using the unsafe keyword in Rust does not disable the borrow checker or any other safety checks enforced by the language. Rust will verify their validity even when working with references inside an unsafe block. The primary role of unsafe is to grant access to certain features not subject to the compiler's memory safety checks. However, Rust still offers a degree of safety within the confines of an unsafe block.

Moreover, it's of utmost importance to realize that the usage of unsafe doesn't inherently suggest that the code enclosed within the block is dangerous or will result in memory safety problems. Instead, the onus is on the programmer to guarantee that any code within an unsafe block is properly structured and complies with the regulations of memory safety.

Example

Following is an example of using and calling the unsafe function.

// Unsafe function
unsafe fn unsafe_access(arr: &[i32], index: usize) -> i32 {
*arr.get_unchecked(index)
}
fn main() {
let data = [10, 20, 30, 40, 50];
// Normal indexing
let s_index = 2;
let s_result = data.get(s_index).unwrap();
println!("Safe access: {:?}", s_result);
// Unsafe indexing
let u_index = 10;
let u_result = unsafe { unsafe_access(&data, u_index) };
println!("Unsafe access: {}", u_result);
}

Explanation

The line-by-line explanation of the code above is as follows:

  • Line 2: We declare an unsafe function named unsafe_access. The function takes two parameters: a arr of i32 values and an index of type usize. The purpose of this function is to access elements of the slice directly without performing bounds checking.

  • Line 4: We use the get_unchecked method on the arr to access the element at the given index. The * before arr.get_unchecked(index) dereferences the pointer returned by get_unchecked, giving us the value at the specified index.

  • Lines 7–8: We define the main function and create an array data containing i32 values.

  • Line 11: We declare a variable s_index and assign the value 2 to it.

  • Line 12: Using the .get() method on the array data, we attempt to access the element at the index s_index.

  • Line 13: We use println! to print the result.

  • Line 16: We declare a variable u_index and assign the value 10 to it.

  • Line 17: We call the unsafe_access function using an unsafe block. Inside the unsafe block, we pass a reference to the data array and the u_index to the unsafe_access function. Since unsafe_access assumes the index is within bounds, there are no bounds checking, and it directly accesses memory at the index 10, which is out of bounds for the data array.

When to use unsafe

Developers should use unsafe judiciously and avoid it whenever possible. It should only be used when necessary for low-level operations, interfacing with external code, or performance optimization. It's crucial to thoroughly understand the implications and risks of using unsafe code and to keep unsafe blocks small and well-documented. Rust code should strive to provide safe abstractions over unsafe code whenever possible to minimize potential safety issues.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved