Handle errors with unwrap() in Rust

Pascal Precht
InstructorPascal Precht

Share this video with your friends

Send Tweet

In this lesson we'll take a look at the simplest way of handling error in Rust using the unwrap() function.

Ya Zhuang
Ya Zhuang
~ a year ago

line 14 should set b:u32 to second.trim().parse().unwrap() right?

Pascal Precht
Pascal Prechtinstructor
~ a year ago

Hi @Ya

you're absolutely right. This is a typo and also a typo in the video. Will update the embedded code!

J. Matthew
J. Matthew
~ a year ago

I noticed you specified the type of a and b when defining them, in a way you hadn't in previous lessons or even for other declarations in this lesson. let a:u32 = and let b:u32 = vs. let mut first = (which could also have been written let mut first:String = ). Was that just for making the code more readable, or did it serve some other purpose?

The code seems to run the same if I take those explicit types out, and hovering over them reveals that the editor has still inferred a type of u32. Reviewing the description of parse, it mentions that the method can handle many different types, so it gives you the option of casting the intended return type with this wild syntax (the "turbofish," apparently): parse::<u32>(); but that isn't what you used, so I'm curious.

I also think it's interesting that the type is u32 rather than number or int or some such. I see there are other options like u128; does it have to do with the complexity of the number in question?

Pascal Precht
Pascal Prechtinstructor
~ a year ago

Hey @Matthew,

excellent question again (keep them coming! :D).

Was that just for making the code more readable, or did it serve some other purpose?

So generally, the Rust compiler will infer the type of an expression if it can using Type Inference (I'll talk about it in a video collection about basic types that I'm working on right now). The line

let first = new String::new();

For example, doesn't need a type annotation, because the compiler knows that, when you create a String using String::new(), the type will be a String.

In other cases however, Rust can't infer the type. That's the case in this line here:

let a = first.trim().parse().unwrap();

It turns out that parse() is actually "generic" which means it can work on multiple types. If you're not telling the compiler what type you want to parse to, it can't know and will ask you to provide an annotation. Take this example:

let val = "5"; // looks like a number but is actually a &str

let number = val.parse().unwrap();

This code will not compile because Rust can't know what, what we're aiming for here is a number type. A &str (which is a string slice) can also be parsed to a String, or any other number type in this case.

Doing

let number: i32 = val.parse().unwrap();

Tells the compiler that we're trying to parse to a signed integer of 32 bits. Another way to provide the type information is to use the turbo fish syntax:

let number = val.parse::<i32>().unwrap();

Which syntax you use in this case doesn't matter, but there are cases, especially when dealing with more functional code, where the return value of parse() isn't the final value but gets passed to another function, where turbofish syntax is necessary.

For the sake of this lesson I didn't want to make it more complex than needed, but decided to cover this in another lesson that I can then link here.

The code seems to run the same if I take those explicit types out, and hovering over them reveals that the editor has still inferred a type of u32. Reviewing the description of parse, it mentions that the method can handle many different types, so it gives you the option of casting the intended return type with this wild syntax (the "turbofish," apparently): parse::<u32>(); but that isn't what you used, so I'm curious.

Ha, I should've finished reading your comment first before responding! :D You got it!

I also think it's interesting that the type is u32 rather than number or int or some such. I see there are other options like u128; does it have to do with the complexity of the number in question?

Also something I'll cover in the collection I'm working on right now but the bottom line is that there are signed and unsigned integers of different fixed sizes:

  • u8 - Unsigned 8 bit integer

  • u16 - Unsigned 16 bit integer

  • u32 - Unsigned 32 bit integer ... and so on.

  • i8 - Signed 8 bit integer

  • i16 - Signed 6 bit integer ... you get the idea.

Depending on how big the values are you want to store, you might wanna use one type over the other. Rust defaults to i32 if none is given.

Hope this makes sense!

J. Matthew
J. Matthew
~ a year ago

Which syntax you use in this case doesn't matter, but there are cases, especially when dealing with more functional code, where the return value of parse() isn't the final value but gets passed to another function, where turbofish syntax is necessary.

Ah, I see, so specifying the type in the declaration does do the same thing that turbofish would have done, at least in this case. That makes sense, as does the rest of your great response. Thanks!