How do I check if both variables are both Some? - rust

I am confused about the Some(T) keyword.
I want to check for two variables, if the value is defined (not None). If that is the case, the value of this variables is processed.
I know the match pattern which works like this:
match value {
Some(val) => println!("{}", val),
None => return false,
}
If I use this pattern, it will get very messy:
match param {
Some(par) => {
match value {
Some(val) => {
//process
},
None => return false,
}
},
None => return false,
}
This can't be the right solution.
The is a possibility, to ask if the param and value is_some() That would effect code like that:
if param.is_some() && value.is_some() {
//process
}
But if I do it like that, I always have to unwrap param and value to access the values.
I thought about something like this to avoid that. But this code does not work:
if param == Some(par) && value == Some(val) {
//process
}
The idea is that the values are accessible by par and val like they are in the match version.
Is there any solution to do something like this?

If I have several Option values to match, I match on a tuple of the values:
enum Color {
Red,
Blue,
Green,
}
fn foo(a: Option<Color>, b: Option<i32>) {
match (a, b) {
(Some(Color::Blue), Some(n)) if n > 10 => println!("Blue large number"),
(Some(Color::Red), _) => println!("Red number"),
_ => (),
}
}
fn main() {
foo(Some(Color::Blue), None);
foo(Some(Color::Blue), Some(20));
}
This allows me to match the combinations that are interesting, and discard the rest (or return false, if that is what you want to do).

If your function is processing multiple Option values, and would like to discard them if they're not Some, your function could return an Option itself:
fn foo(param: Option<usize>, value: Option<usize>) -> Option<usize> {
let result = param? + value?;
Some(result)
}
This will short-circuit the function in case there's a None value stored in either param or value.
Please read the book for more information on the ? operator.
If your function can't return an Option, you can still get away with destructuring using if let or match:
let x = if let (Some(p), Some(v)) = (param, value) {
p + v
} else {
return 0;
}
let x = match (param, value) {
(Some(p), Some(v)) => p + v,
(Some(p), _) => p,
(_, Some(v) => v,
_ => return 0,
}
Please read What is this question mark operator about? for more information on the ? operator
Please read this chapter in Rust by Example for more information on destructuring multiple things at once

There's a couple more alternatives not yet listed:
If you're willing to use experimental features (and hence the nightly compiler) you can use a try block as an alternative of extracting a function.
#![feature(try_blocks)]
fn main() {
let par: Option<f32> = Some(1.0f32);
let value: Option<f32> = Some(2.0f32);
let x: Option<f32> = try { par? + value? };
println!("{:?}", x);
}
Another alternative is to use map which only applies if the value is not None
let x: Option<f32> = par.map(|p| value.map(|v| p + v));

Related

Macro to match specific identifiers for inline lookups

Okay, writing my absolute first project in Rust. So, I have something like the following sort of setup:
use phf;
use std::str;
struct Values {
a: Option<char>,
b: Option<char>,
c: Option<char>
}
static MAPPING: phf::Map<&'static str, Values> = phf::phf_map! {
"some_key" => Values {
a: Some('a'),
b: Some('b'),
c: None
},
"some_other_key" => Values {
a: Some('1'),
b: None,
c: None
},
"some_third_key" => Values {
a: None,
b: Some('x'),
c: Some('y')
}
}
static NULL_VALUES: Values = Values {
a: None,
b: None,
c: None
}
// Should return a &str for any given key/val
#[macro_export]
macro_rules! get_value {
($key: ident, $val: ident) => {{
use crate::values::MAPPING;
use std::str;
let result = MAPPING.get("$key");
if let Some(r) = result {
if let Some(c) = r.$val {
if let Ok(s) = str::from_utf8(%[c as u8]) { s } else { "" }
} else { "" }
} else { "" }
}}
}
Which, it works, but it's just so much code and seeming like a whole lot of runtime overhead for no other reason than to organise some static values to avoid having to remember them all (in reality there are quite a lot and they're all raw codepoints). What I would love to be able to do is to just have a macro that takes a specific key/val and simply inlines either a known value or an empty value, but as far as I can tell there isn't any way to match a macro on a specific identifier, only any identifier... Is there any way that I can move all these lookups from runtime to compile time?
Macros can pattern match against specific identifiers — just don't use $.
macro_rules! get_value {
(some_key, a) => { Some('a') };
(some_key, b) => { Some('b') };
(some_key, c) => { None };
(some_other_key, a) => { Some(1) };
// ...
}
However, are you sure you don't want to just define a bunch of constants?
const SOME_KEY_A: Option<char> = Some('a');
const SOME_KEY_B: Option<char> = Some('b');
Or expose the Values struct you already designed, which would then be accessed like SOME_KEY.a:
const SOME_KEY: Values = Values {
a: Some('a'),
b: Some('b'),
c: None
};
That way, readers don't have to understand your macro to know that the data is just a constant. This will make your code easier to read and modify.

How to find X in a HashMap of HashMaps?

Given that I know ParentId and ChildId, how would I find the UserId if the hashmap is:
HashMap<ParentId, HashMap<ChildId, HashMap<UserId, Foobar>>>
As my knowledge about Rust is pretty basic, I found that the following works but it's quite verbose, as I don't know any better:
match foobar.get(&pid) {
Some(data1) => {
println!("Found 1: {:?}", data1);
match data1.get(&cid) {
Some(data2) => {
println!("Found 2: {:?}", data2);
...and so on
},
_ => println!("Not found")
}
},
_ => println!("Not found")
}
I've also attempted chained get but it's tricky and did not find how to do it correctly:
foobar
.get(pid)?
.get(cid)?
.get(to_find)
What can I try next?
You can use Option::and_then to chain operations that return Option<_>:
let _: Option<&Foobar> = foobar.get(&pid).and_then(|map| map.get(&cid)).and_then(|map| map.get(&to_find));
Example:
use std::collections::HashMap;
fn main() {
let map: HashMap<i32, HashMap<bool, HashMap<String, bool>>> = HashMap::new();
let _: Option<&bool> = map
.get(&123)
.and_then(|map| map.get(&true))
.and_then(|map| map.get("foo"));
}
Playground
Your try with ? is also correct but it'll only work in a function that returns an Option as it returns None from the function the expression is in if any value is None, which is probably the error you're getting.
fn get(map: &HashMap<i32, HashMap<bool, HashMap<String, bool>>>) -> Option<bool> {
map.get(&123)?.get(&true)?.get("foo").cloned()
}
Edit: As #Jmb pointed out in a comment below, another option is to create and immediately call a closure so you can use the ? operator which could be more readable in certain cases:
let _: Option<&bool> = (|| map.get(&123)?.get(&true)?.get("foo"))();

Is there a less verbose way to extract values from Options in Rust

I find myself doing something like the following a lot:
fn foo() -> Result<i32, String> {
let cur = match something_that_returns_an_option() {
Some(cur) => cur,
None => return Err("Some error"),
};
// use `cur`
1
}
If I need several variables, I'm left with this pattern over and over again, or nested if lets/matches.
I there a more ergonomic way to handle repeatedly extracting values from Options?
You are looking for Option::ok_or. It lets you map an Option into a Result with the provided error. Combined with the ? operator you clean things up nicely:
fn foo() -> Result<i32, String> {
let cur = something_that_returns_an_option().ok_or("some error")?;
Ok(cur + 1)
}
Playground
Option::ok_or_else might also be helpful, as it evaluates the error branch lazily.
In your example you want to not just continue, break or return a regular value, but return an error. For that particular case, the Aiden4's answer is the way to go. But I've been in situations where I want to unwrap or (in the case of None) directly continue, break or return a non-error value. Rust (still) doesn't provide a short and concise way to do exactly that.
Here is a "one-liner" which kinda does the trick, but is still a bit verbose:
let v = if let Some(d) = some_option_value { d } else { continue; };
If you want a shorter solution, here are two options...
A macro
You can write a macro like this:
macro_rules! unwrap_or {
($e:expr, $or_do_what:expr) => {
if let Some(d) = $e { d } else { $or_do_what }
};
}
That will allow you to write code like this:
let options = vec![Some(74), None, Some(9)];
for o in options {
let v = unwrap_or!(o, continue);
// ...
}
That's a trivial example, but I think the biggest benefit can come if you need to perform multiple checks, so that instead of writing something "idiomatic" like this:
for thing in things {
if let Some(a) = thing {
// ...
if let Some(b) = a.another_opt {
// ...
if let Some(c) = a.yet_another_opt {
// ...
}
}
}
}
, you can simplify the code by avoiding the nesting of multiple blocks like this:
for thing in things {
let a = unwrap_or!(thing, continue);
// ...
let b = unwrap_or!(a.another_opt, continue);
// ...
let c = unwrap_or!(a.yet_another_opt, continue);
// ...
}
Whether that's a good practice is subjective, of course.
let...else (unstable)
There is an unstable let...else feature, which is intended to solve the problem. Example:
#![feature(let_else)]
...
let options = vec![Some(74), None, Some(9)];
for o in options {
let Some(v) = o else { continue };
println!("v = {v}");
}

How do I match a vector of tuples and its content using a single check?

I want to match a vector of tuples and its content using a single check. If the value inside the tuple is equal to some_value (a usize) then I do something, for every other case I do something else.
I handle it like this with basic logic:
if myvector.is_empty() {
// do action 1
} else if myvector.last().unwrap().0 == some_value {
// do action 2
} else {
// do action 1
}
This does what I want, but I feel there's a more idiomatic way to do it.
I've been trying with match:
match myvector.last() {
Some(t) => match t.0 == some_value {
true => unimplemented!("do action 2"),
false => unimplemented!("do action 1"),
},
None => unimplemented!("do action 1"),
}
This also works, but I'm trying to figure out a better syntax to cover a single case once only (action1).
You can use a slice pattern to extract the last element, and deconstruct the tuple directly in the pattern:
fn test_last(v: &[(u32, u32)], testval: u32) -> u32 {
match v {
[.., (val, _)] if *val == testval => {
// action 2
}
_ => {
// action 1
}
}
}
Playground
I got the following to work:
match myvector.last() {
Some(t) if t.0 == some_value => unimplemented!("do action2"),
_ => unimplemented!("do action1"),
}
I would do:
if let Some(_) = my_vector.last().filter(|(n, _)| n == some_value) {
// do action 2
} else {
// do action 1
}

How can I check a variable entered by user is a number (int, float)?

How can I check a variable (entered by user) is a number such as an int, float, or something else?
I want to do this with a match expression:
let mut input = String::new();
io::stdin().read_line(&mut input);
let result = match input {
// !!??
}
is it possible by match?
If you want to match against something then you need something to destructure on. You can match against string slices, but only a finite set, so that doesn't help here.
So let's have an enum to match on:
enum Value {
Int(isize),
Float(f64),
}
use Value::*; // Make the variants available without Value::
Then you need to parse the string into the appropriate type. You can use the parse method for each type (if you're happy with Rust's syntax for those; otherwise you might need a fancier parser):
fn parse_string(s: &str) -> Option<Value> {
if let Ok(i) = s.parse() { // inferred as isize from next line
Some(Int(i))
} else if let Ok(f) = s.parse() {
Some(Float(f))
} else {
None
}
}
Note that when trying parses in sequence like this the order matters; "123" would parse as an f64 too.
I'm turning any parse errors into None and using Option rather than Result because it's not clear what the error would be (since each parse can return its own); in a real application I might have a ParseError type.
Now we can match on the result:
fn main() {
let x = "123";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
let x = "123.5";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
}
Runnable playground link

Resources