In the previous blog, I discussed one of the superpowers of Unsafe Rust which is dereferencing a raw pointer. In this blog, we will see another feature of unsafe rust. Rust has wonderful borrowing and ownership rules that take care of all the memory safety issues. It is one of the leading languages. More than 9 companies including Dropbox, Coursera, Figma, npm, Microsoft, Cloudflare, Facebook, Amazon and Discord use Rust for one or the other things. But sometimes Rust does not let us compile the program even though we know that the program is safe to execute. In this case, we use Unsafe Rust. So, let us start and see another feature of Unsafe Rust.
Calling an unsafe method or function
We use an unsafe block to call an unsafe method or function. An unsafe method or function is the same as a normal function except it has an unsafe keyword before its definition. it indicates that it has some requirements to be fulfilled but Rust can not guarantee that the requirements have been fulfilled. So when we define an unsafe function, we need to make sure that the function’s requirements are fulfilled.
To call an unsafe method or function, we simply call the function inside an unsafe block to tell the Rust compiler that the following call is to an unsafe function.
For example, look at the following code.
unsafe fn demo(){}
fn main()
{
demo();
}
This code calls an unsafe function without an unsafe block. So we get the following error.
We simply call the demo
function inside an unsafe block to remove the above error as shown below. By inserting the unsafe
block around our call to dangerous
, we’re asserting to Rust that we’ve read the function’s documentation, we understand how to use it properly.
unsafe fn demo(){}
fn main()
{
unsafe{
demo();
}
}
Creating a Safe Abstraction over Unsafe Code
We need not mark an entire function as unsafe if it contains only some unsafe code. Instead, we can wrap code in an unsafe block inside a safe function. This is a common abstraction technique to define unsafe code.
Have a look at the following code to understand it better.
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = slice.len();
assert!(mid <= len);
(&mut slice[..mid], &mut slice[mid..])
}
fn main(){
let mut vec_to_split = vec![1, 2, 3, 4, 5, 6];
let ref_to_vec = &mut vec_to_split[..];
let (part_1, part_2) = split_at_mut(ref_to_vec,3);
assert_eq!(part_1, &mut [1, 2, 3]);
assert_eq!(part_2, &mut [4, 5, 6]);
}
The above code does nothing but defines a function split_at_mut
that takes one mutable slice over i32 and split it into two by breaking it at the index given as the second argument. This code generates an error when compiled. The error is given below.
The error shows that the function uses two mutable references to the same slice. But what we are doing is completely safe as we are borrowing different parts of the slice which is fine. The Rust compiler doesn’t know this and does not allow us to compile it to avoid any problems. Since we know that this code is fine and will not cause any problem, we know what to do.
To avoid the error and make the program run, we make the following changes to our code.
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 {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
fn main(){
let mut vec_to_split = vec![1, 2, 3, 4, 5, 6];
let ref_to_vec = &mut vec_to_split[..];
let (part_1, part_2) = split_at_mut(ref_to_vec,3);
assert_eq!(part_1, &mut [1, 2, 3]);
assert_eq!(part_2, &mut [4, 5, 6]);
}
We use some unsafe functions to avoid the error and then use an unsafe block so that we do not have to make the whole function unsafe. This is a safe abstraction over unsafe code. We use the from_raw_parts_mut
function which takes a raw pointer and length as arguments and then cut a part of the slice that starts from the pointer.
The add
function is also unsafe as it takes mid
as an argument and returns a raw pointer which starts at mid
. Therefore it must trust that the offset location is also a valid pointer.
So this was all about calling an unsafe method or function.
If you want to read more content like this? Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter: https://bit.ly/2Vdlld7.




1 thought on “Superpowers of Unsafe Rust4 min read”
Comments are closed.