Skip to main content

When and how to use Unwrap in Rust

This article explains how and when to use unwrap, including the best practices to make your Rust code better.

Avoiding Unwrap in Rust #

A common pattern that is sometimes overlooked, is the use of the unwrap functionality in Rust. unwrap allows you to quickly access values returned by various functions such as a function returning a Result objected. Unfortunately, this is a double edge sword, easy to use, but once it fails it causes a panic and breaks the program.

When Firo Solutions launched its bug bounty for its “watcher” platform, we were able to mitigate several security-related
issues thanks to proper error handling(such as not allowing bad functions to return bad data and act on bad data). Being able to properly handle errors is a key part of being a good programmer.

Back to unwrap!

Once a value is returned by rust and we unwrap it and it fails, the execution flow stops and crashes. Luckily there as several good ways to mitigate this, that are often overlooked by a large chunk of programs. In order for us to write better and safer code, here is a quick lesson in error handling:

Lets say we have a function that might brake in runtime that returns a Result:

fn addreward(name: String, amount: u32) -> Result<bool, Error> { // if we have added the reward to the users balance, we want to return true, a simple boolean, and if it fails we want to return an Error 
	let new: u32 = amount+10; // lets append 10 to the amount 
	let wrong_output: bool = storestatus(name, new).unwrap(); // Store the updated balances, Note how we are just unwrap'ing the value and just returns true, no checks 

	let correct_output: bool = match storestatus(name, new ) {
	Some(response) => response,// return the unwraped value if all is good
	Err(e) => return Error(e), // if it fails we want to return an Error 
	}
	let output = correct_output.clone();

    Ok(output)
}

This is function now have the wrong_output, which will break in production and the safe “correct_output” function, which returns an error instead of just breaking, allowing the developer to handle the Error.

Let’s try to now use our function in a correct way:

So the “easiest” and shortest way to use this function, would be to do this:

println!("Calling my function!");
let output: bool = addreward("test".to_string(), 3).unwrap();

Will this compile?
Yes!
Is this safe?
Absolutely not.
Once there is an Error accruing, this program will crash due to the use of unwrap.

So how can we write this function better?

Using match, the Control Flow Operator #

Example:


let output: bool = match myfunction("test".to_string(), 3){ //let's check if we can return a value
	Some(value) => true, // if we get a value from the function we return true
	Err(e) => false, // if the function fails and gives us an error, we return false
	}

if output{
	println!("It worked!");
} else{
	println!("I failed");
};

Using the ? operator #

Rust comes with a nifty feature, which is heavily inspired by Haskell. This feature is very much like a macro, you can see it as a way to shorten match and enabling easy access to a return value. So if we have a result function that gives us a struct and we want to access the first value in that struct, ? comes to the rescue.

Example: Let us say that we can a function that returns an object, a struct in this example:


Simple {
	amount: u32,
	names:	String,
	age: 	u32,
}

fn test() -> Result<Simple, Error> {
	let output: Simple = Simple{amount: 1, names: "james".into(), age: 1337};

/// dangerous things that might break goes one here
	Ok(output)
}

let user_age: u32 = test()?.age;

This expression is pretty much the same as:


let user_age: u32 = match test() {
	Some(value) => value.age, // if we catch a value we want to return the age object in the struct
	Err(e) => Error("My custom error"), // if it fails we cast an error
}

Only use unwrap when you know the function will not fail and even if you choose to use unwrap, make sure you have a well-written comment, explaining why unwrap is safe to use here.

Takeaways #

Do not use unwrap unless you are 110% sure that it is safe. Try to use match and the ? statement.

As of writing this, there are 1.6million rust programs on Github that use unwrap. Source: https://github.com/search?l=Rust&q=unwrap&type=Code

https://doc.rust-lang.org/book/ch09-00-error-handling.html
https://doc.rust-lang.org/rust-by-example/std/result.html
https://www.geeksforgeeks.org/rust-match-operator/
https://doc.rust-lang.org/book/ch06-02-match.html