I am a very novice programmer and very new Rustacean. I'm working my way through Steve Donovan's "A Gentle Introduction to Rust" (which I highly recommend for anyone who may struggle with "The Rust Programming Language" like I did) using Rust v1.24.
In the section on Optional Values, he covers the unwrap_or
method for the Option
type to retrieve values from a slice as a shortcut to using just unwrap()
. He states:
The types must match up - get
returns a reference. so you have to make up a &i32
with &-1
. Finally, again use *
to get the value as i32
.
let last = *slice.get(5).unwrap_or(&-1);
However, in my code below, I have found that this method works both with and without the *
.
fn main() {
let foo = [10, 20, 30, 40];
for i in 0..foo.len()+1 {
println!("{} {}", i, foo.get(i).unwrap_or(&-1));
// this also works:
// println!("{} {}", i, *foo.get(i).unwrap_or(&-1));
}
}
Can someone please help me understand why I do or do not need the *
as well as the role the &
plays in the unwrap_or
method? My fear is that if I do not understand the concepts of these methods and borrowing, I am not going to get very far.
Thanks in advance!
The distinction is better exemplified if you do this:
fn main() {
let foo = [10, 20, 30, 40];
for i in 0..foo.len() + 1 {
takes_i32(*foo.get(i).unwrap_or(&-1));
}
}
fn takes_i32(_: i32) {}
takes_i32
takes an i32
, not a &i32
. The reason println!
works is because the Display
trait is implemented for i32 and &i32 (or more generally, for any T
that implements Display
, so does &T
automatically). println!
also does not require values - it can work with references as well.
The *
(dereference) copies out the i32
from the &i32
you get back from the slice; since i32
is a Copy
type, this is perfectly fine.
For Clone
types (which all Copy
types are as well), there's also Option::cloned()
which gives you back a value, rather than reference. So you can do:
// type ascription only used for illustration - you don't actually need it
let x: i32 = foo.get(i).cloned().unwrap_or(-1);
1 Like
This really helped, @vitalyd! The code you supplied compiled fine but when I removed the dereference (*
), I got the following error:
error[E0308]: mismatched types
--> takesi32.rs:5:19
|
5 | takes_i32(foo.get(i).unwrap_or(&-1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected i32, found &{integer}
| help: consider dereferencing the borrow: `*foo.get(i).unwrap_or(&-1)`
|
= note: expected type `i32`
found type `&{integer}`
error: aborting due to previous error
I did not know that the println!
macro works with both i32
and &i32
so when I changed my code to the following below, I got a similar error as I assume the compiler is treating x
as i32
but get
and unwrap_or
return a reference (&i32
).
fn main() {
let foo = [10, 20, 30, 40];
let mut x = 0;
for i in 0..foo.len()+1 {
x = foo.get(i).unwrap_or(&-1);
println!("{} {}", i, x);
}
}
Whenever you see {integer}
in error messages, it just means type inference in the compiler hasn’t fully concluded which exact integer type you’re after.
To make the compiler spit out a very precise type in the error message, you can unambiguously tell it which types you want:
fn main() {
// we’ll specify i32 here
let foo = [10i32, 20, 30, 40];
// and here
let mut x = 0i32;
for i in 0..foo.len()+1 {
x = foo.get(i).unwrap_or(&-1);
println!("{} {}", i, x);
}
}
As for this part, println! works on references - whatever value you give it, it essentially does an &
on it. So if you give it &5i32
, it will make it an &&5i32
. Then some pattern matching comes into play (I’m glossing over this to not bog down in those details - we can discuss that separately if you’re interested) that strips it back down to &5i32
(you can try printing &&&&&&&&&&&&5i32
and it’ll also work
)
The reason println does this is to not move (aka consume) the value away from you. That’s not interesting for Copy types, but if you wanted to print say a String (non Copy), you’d want to be able to use it afterwards.
I know that I am not the first one to say this but I really like how helpful the rust compiler error messages are:
error[E0308]: mismatched types
--> src/main.rs:8:13
|
8 | x = foo.get(i).unwrap_or(&-1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected i32, found &i32
| help: consider dereferencing the borrow: `*foo.get(i).unwrap_or(&-1)`
|
= note: expected type `i32`
found type `&i32`
error: aborting due to previous error
Indeed it does!