What is the idiomatic way to do something when an Option is either None, or the inner value meets some condition? - rust

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.

// in None case
// │ in Some(_) case
// ┌┴─┐ ┌───────────────────┐
if foo.map_or(true, |foo_val| foo_val < 5) {
// ...
}
For more information see Option::map_or.

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:
when foo is Some with a value smaller than 5;
when 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.

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
}

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

The matches! macro seems like a good fit:
if !matches!(foo, Some(a) if a>=5) { foo.replace(5) }
Rust Playground

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));

With filter and or,
foo = foo.filter(|a| *a >= 5)
.or(Some(5));

There is the unstable method Option::is_some_and that has been built for exactly this purpose:
if foo.is_some_and(|foo_val| foo_val < 5) {
// ...
}
As it's unstable, it's currently only usable on nightly. See the tracking issue for up to date information.

Related

Rust lifetimes in if statement

I have an if statement in a for loop, and I want it to create a variable with the lifetime of that iteration of the for loop.
for condition_raw in conditions_arr {
println!("{}", condition_raw);
let matching = !condition_raw.contains('!');
if matching {
let index = condition_raw.find('=').unwrap_or_default();
} else {
let index = condition_raw.find('!').unwrap_or_default();
}
let key = &condition_raw[..index];
}
let key = &condition_raw[..index]; currently throws cannot find value index in this scope
not found in this scope rustc E0425
I'll ignore the condition variable which does not seem to be used at all in your example.
A let statement creates a binding that holds at most for the current scope. For this reason, when you create the index variable inside the if, you are not making it accessible anywhere else. There are two ways to solve this issue.
The first way is to explicitly declare index as being part of the outer scope, and only define it inside the if statement.
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index;
if matching {
index = condition_raw.find('=').unwrap_or_default();
} else {
index = condition_raw.find('!').unwrap_or_default();
}
let key = &condition_arr[..index];
}
There is no risk of accidentally not defining index, since Rust will make sure that index is defined (exactly once) in all possible branching of your code before it is used. Yet, it's not a pretty solution because it violates a "locality" principle, that is that pieces of code should have effects on or pieces of code that are sufficiently close. In this case, the let index; is not too far from its definition, but it could be arbitrarily far, which makes it painful for someone who reads your code to remember that there is a declared but not yet defined.
Alternatively, you could use the fact that most things in Rust are expressions:
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index = if matching {
condition_raw.find('=').unwrap_or_default();
} else {
condition_raw.find('!').unwrap_or_default();
}
let key = &condition_arr[..index];
}
But, in fact, you could factorize your code even more, which is usually better:
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index = condition_raw.find(if matching {
'='
} else {
'!'
}).unwrap_or_default();
let key = &condition_arr[..index];
Or, even more
for condition_raw in conditions_arr {
let index = condition_raw
.find('!')
.or_else(|| condition_raw.find('='))
.unwrap_or_default();
let key = &condition_arr[..index];
}
An idiomatic way to assign variables from an if else statement is as follows:
let index: usize = if matching {
condition_raw.find('=').unwrap_or_default()
} else {
condition_raw.find('!').unwrap_or_default()
};
Idiomatic way of assigning a value from an if else condition in Rust
In Rust, an if/else block is an expression. That is to say, the block itself has a value, equivalent to the last expression in whatever section was executed. With that in mind, I would structure your code like this:

Is there an idiomatic way of chaining Option in Rust?

When deserializing deeply nested structures (e.g. from JSON), it's not uncommon to have to traverse multiple Option types.
For example:
let foo = Foo {
x: Some(Bar {
y: Some(Baz {
z: Some(42),
})
})
};
Is there an idiomatic way of chaining Option to access deeply nested values?
So far I have the following, but neither are as concise as foo.x?.y?.z in other languages that support optional-chaining:
let z = foo.x.as_ref().and_then(|x| x.y.as_ref()).and_then(|y| y.z);
let z = foo.x.as_ref().and_then(|x| x.y.as_ref()?.z);
let z = (|| foo.x.as_ref()?.y.as_ref()?.z)();
It looks like the try_block feature might be a good fit, but it's currently unstable.
let z = try { foo.x.as_ref()?.y.as_ref()?.z };
As you say, the try block would be perfect for this.
In the meantime you can take advantage of the fact that ? works in functions, and wrap your expression in a closure and call it:
let z = (|| foo.x.as_ref()?.y.as_ref()?.z )();
You can write a simple macro to make it a bit nicer:
macro_rules! tryit {
($($e: tt)+) => {
(|| { $($e)+ })()
}
}
Which works basically the same as the try block:
let z = tryit! { foo.x.as_ref()?.y.as_ref()?.z };
Another option if you don't like the immediately called closure is to use a crate like map_for or mdo. Example with map_for:
use map_for::option::FlatMap;
let z = map_for!{
x <- foo.x.as_ref();
y <- x.y.as_ref();
=> y.z }:
Disclaimer: I wrote the map_for crate.

error handling when unwrapping several try_into calls

I have a case where I need to parse some different values out from a vector.
I made a function for it, that returns a option, which either should give a option or a None, depending on whether the unwrapping succeeds.
Currently it looks like this:
fn extract_edhoc_message(msg : Vec<u8>)-> Option<EdhocMessage>{
let mtype = msg[0];
let fcnt = msg[1..3].try_into().unwrap();
let devaddr = msg[3..7].try_into().unwrap();
let msg = msg[7..].try_into().unwrap();
Some(EdhocMessage {
m_type: mtype,
fcntup: fcnt,
devaddr: devaddr,
edhoc_msg: msg,
})
}
But, I would like to be able to return a None, if any of the unwrap calls fail.
I can do that by pattern matching on each of them, and then explicitly return a None, if anything fails, but that would a lot of repeated code.
Is there any way to say something like:
"if any of these unwraps fail, return a None?"
This is exactly what ? does. It's even shorter than the .unwrap() version:
fn extract_error_message(msg: Vec<u8>) -> Option<EdhocMessage> {
let m_type = msg[0];
let fcntup = msg[1..3].try_into().ok()?;
let devaddr = msg[3..7].try_into().ok()?;
let edhoc_msg = msg[7..].try_into().ok()?;
Some(EdhocMessage {
m_type,
fcntup,
devaddr,
edhoc_msg
})
}
See this relevant part of the Rust Book.

Check equaling enum without parameter

I use enums but can't find good way to check eqauling.
enum Turn {
A(value:Int);
B(value:Int);
}
class Test {
static function main() {
var turn = Turn.A(100);
//I want to Check turn is Turn.A(any value) without using 'switch'.
if (turn == Turn.A) ...
}
}
Is there any good and simple way to checking?
You can use the .match() function:
if (turn.match(Turn.A(_)))
I haven't tested this, but it might be faster using Type class:
if (Type.enumConstructor(turn) == "A") ...
Because it is unsafe ("A" could be a typo), I suggest to use ExprTools:
import haxe.macro.ExprTools.*;
if (Type.enumConstructor(turn) == toString(macro A)) ...
There is another way, but I don't think it is faster :
if (Type.enumIndex(turn) == Type.enumIndex(A(0))) ...
And you might get condition evaluated to true for different enums:
enum Color { Red; }
if (Type.enumIndex(turn) == Type.enumIndex(Red)) ... // true

Does Rust 2018 support "if let" chaining?

I'm parsing a vector of tokens, each of enum type. This means I get a lot of code like:
if v.len() >= 3 {
if let Token::Type1(value1) = &v[0] {
if let Token::Type2(value2) = &v[1] {
if let Token::Type(value3) = &v[2] {
return Parsed123(value1, value2, value3);
}
}
}
}
This is pretty ugly - and I've worked out that I can do this to make it a little nicer:
if v.len() >= 3 {
if let (Token::Type1(value1), Token::Type2(value2), Token::Type3(value3)) =
(&v[0], &v[1], &v[2])
{
return Parsed123(value1, value2, value3);
}
}
But honestly, its not much better.
However, there's some closed issues / RFCs for chaining these conditions and "if let" bits in what feels a lot more ergonomic way -- Tracking issue for eRFC 2497 "if- and while-let-chains take 2" and Support && in if let expressions -- this would let me write something like:
if v.len() >= 3 &&
let Token::Type1(value1) = &v[0] &&
let Token::Type2(value2) = &v[1] &&
let Token::Type3(value3) = &v[2]
{
return Parsed123(value1, value2, value3);
}
However, I can't seem to get this to compile in my copy of nightly Rust with edition="2018" (exact version is 1.32.0-nightly (653da4fd0 2018-11-08)). So either I've got the syntax wrong or I've misinterpreted the RFCs / issues and this feature hasn't landed yet. Either way, I'd love some info on how this feature stands.
RFC #2497 has not been implemented yet. The GitHub issue you linked is only for describing how to deal with the ambiguity.
To enable the second interpretation in the previous section a warning must be emitted in Rust 2015 informing the user that [...] will both become hard errors, in the first version of Rust where the 2018 edition is stable, without the let_chains features having been stabilized.
So no, you cannot use the syntax yet, but instead use tuples as a workaround, as you already did.
if v.len() >= 3 {
if let (Token::Type1(value1), Token::Type2(value2), Token::Type3(value3)) =
(&v[0], &v[1], &v[2])
{
return Parsed123(value1, value2, value3);
}
}
While hellow is correct that RFC #2497 is not yet supported in 2018 (and 2015), I felt the if_chain library mentioned by Michail was worthy of an answer.
The if_chain library provides a macro that transforms some code that is almost in the form of RFC #2497 into valid Rust.
You can write:
if_chain! {
if v.len() >= 3;
if let Token::Type1(value1) = &v[0];
if let Token::Type2(value2) = &v[1];
if let Token::Type3(value3) = &v[2];
then {
return Parsed123(value1, value2, value3);
}
}
which the compiler treats as:
if v.len() >= 3 {
if let Token::Type1(value1) = &v[0] {
if let Token::Type2(value2) = &v[1] {
if let Token::Type(value3) = &v[2] {
return Parsed123(value1, value2, value3);
}
}
}
}
As mentioned in the comments by L.F., in 2020 there is now another alternative. It still doesn't give us chained if let, but does allow us to match on slices - which is enough to make this example quite neat. The code could now be written as
if let [Token::Type1(value1), Token::Type2(value2), Token::Type3(value3), ..] = v {
return Parsed123(value1, value2, value);
}

Resources