Reputation: 3075
Is there a more idiomatic way to express something like the following?
fn main() {
let mut foo: Option<u8> = None;
match foo {
Some(foo_val) if ! (foo_val < 5) /* i.e. the negation of my acceptance condition */ => {}
_ => { foo.replace(5); }
}
}
It seems like most of the time there's an alternative to having an arm that doesn't do anything, but I've been unable to find one for this particular case.
What I'd like to say is the more direct if foo.is_none() || /* some way to extract and test the inner value */ { ... }
, or perhaps some chaining trick that's eluding me.
Upvotes: 34
Views: 22194
Reputation: 131
As of Rust 1.82.0, we have Option::is_none_or
for this exact scenario:
foo.is_none_or(|foo_val| foo_val < 5)
Upvotes: 3
Reputation: 101
The matches! macro seems like a good fit:
if matches!(foo, Some(a) if a>=5) { foo.replace(5) }
Upvotes: 6
Reputation: 88696
Since Rust 1.70.0 (2023-06-02), Option::is_some_and
can be used. It has been built for the opposite use case as in the question: returning false
if it is None
:
if foo.is_some_and(|foo_val| foo_val < 5) {
// ...
}
Upvotes: 6
Reputation: 11728
There are many ways to do it. One of the simplest (and arguably most readable) is something like this:
if foo.unwrap_or(0) < 5 {
...
}
The above will be true in both cases:
foo
is Some
with a value smaller than 5
;foo
is None
.In some more complex scenarios, where the "default" value needs to be calculated and performance is critical, you might want to consider unwrap_or_else
.
As Lukas suggested, the map_or
method can also be used. Note that arguments passed to map_or
are eagerly evaluated, so if performance is critical, you might want to consider map_or_else
as an alternative.
The is_some_and
method has been stabilized in Rust 1.70. Here is an example:
if name.is_some_and(|s| s.len() > 50) {
println!("This is a long name!");
}
In many cases that should be more readable than map_or
/ map_or_else
.
Upvotes: 30
Reputation: 88696
// in None case
// │ in Some(_) case
// ┌┴─┐ ┌───────────────────┐
if foo.map_or(true, |foo_val| foo_val < 5) {
// ...
}
For more information see Option::map_or
.
Upvotes: 65
Reputation: 25844
I'll throw in another solution just for fun....
foo = foo.
or(Some(5)). // if None return Some(5)
map(|x| if x<5 { 5 } else { x });
or (for this specific example)
foo = foo.
or(Some(5)). // if None return Some(5)
map(|x| u8::max(x, 5));
Upvotes: 3
Reputation: 23319
You can do it with filter
(using the negation of your condition) and is_none
:
if foo.filter(|&x| !(x < 5)).is_none() {
// Here either foo was None or it contained a value less than 5
}
Upvotes: 15
Reputation: 2810
I'm not sure I completely understand your question but you can try something like that:
fn main() {
let foo: Option<u8> = None;
let result = foo.filter(|foo_val| !(*foo_val < 5) ).unwrap_or(5);
println!("Result: {result}");
}
More example on Playground
Upvotes: 6