Options
Say we wanted to write an integer divide that avoided crashing on division by zero. In Perl, we might write this.
sub safediv($x, $y) {
return if $y == 0;
int($x / $y);
}
This returns an undefined value when we try to divide by zero. We might call it like so
my $d = safediv(1, 2);
say $d if defined $d;
The value that we get back might be undef
, so we check first before printing it.
In Rust, we might write this.
#![allow(unused_variables)] fn main() { fn safediv(x: i32, y: i32) -> Option<i32> { if y == 0 { return None; } Some(x/y) } }
This returns an Option<i32>
. It will be None
if we try to divide by zero. Otherwise it will have our integer wrapped in a Some
. We might call it like so
fn main() { let option_d = safediv(1, 2); match option_d { Some(d) => println!("{}", d), None => {}, } } fn safediv(x: i32, y: i32) -> Option<i32> { if y == 0 { return None; } Some(x/y) }
Here we match
on our Option
. If it is a Some
, then we destructure it as part of the match; we can access our integer as d
. If it is None
, then we do nothing.
Another way would be with if let
.
fn main() { if let Some(d) = safediv(1, 2) { println!("{}", d); } } fn safediv(x: i32, y: i32) -> Option<i32> { if y == 0 { return None; } Some(x/y) }
This implicitly does the destructuring match and only does what's inside if it succeeds.
We can also unwrap
an option. If we unwrap a Some
, we get what's inside. If we unwrap a None
, it panics.
fn main() { let d = safediv(1, 2).unwrap(); println!("{}", d); } fn safediv(x: i32, y: i32) -> Option<i32> { if y == 0 { return None; } Some(x/y) }
This is kind of like this Perl
my $d = safediv(1, 2) // die;
say $d;
Similarly, we can expect
, which is like unwrap with an added message.
fn main() { let d = safediv(1, 2).expect("Cannot divide by zero!"); println!("{}", d); } fn safediv(x: i32, y: i32) -> Option<i32> { if y == 0 { return None; } Some(x/y) }
This is kind of like this in Perl
my $d = safediv(1, 2) // die "Cannot divide by zero!";
say $d;