In this lesson we'll take a look at the simplest way of handling error in Rust using the unwrap()
function.
line 14 should set b:u32
to second.trim().parse().unwrap()
right?
Hi @Ya
you're absolutely right. This is a typo and also a typo in the video. Will update the embedded code!
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?
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!
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!