How to group 'Option' assignments in Rust? - rust

I have a block of code where multiple optional variables need to be assigned at once. There is very little chance any of the values will be None, so individually handing each failed case isn't especially useful.
Currently I write the checks like this:
if let Some(a) = foo_a() {
if let Some(b) = foo_b() {
if let Some(c) = foo_c() {
if let Some(d) = foo_d() {
// code
}
}
}
}
It would be convenient if it was possible to group assignments. Without this, adding a new variable indents the block one level, making for noisy diffs and causes unnecessarily deep indentation:
if let Some(a) = foo_a() &&
let Some(b) = foo_b() &&
let Some(c) = foo_c() &&
let Some(d) = foo_d()
{
// code
}
Is there a way to assign multiple Options in one if statement?
Some details worth noting:
The first function that fails should short circuit and not call the others. Otherwise, it could be written like this:
if let (Some(a), Some(b), Some(c), Some(d)) = (foo_a(), foo_b(), foo_c(), foo_d()) {
// Code
}
Deep indentation could be avoided using a function, but I would prefer not to do this since you may not want to have the body in a different scope...
fn my_function(a: Foo, b: Foo, c: Foo, d: Foo) {
// code
}
if let Some(a) = foo_a() {
if let Some(b) = foo_b() {
if let Some(c) = foo_c() {
if let Some(d) = foo_d() {
my_function(a, b, c, d);
}
}
}
}

As #SplittyDev said, you can create a macro to get the functionality you want. Here is an alternate macro-based solution which also retains the short-circuiting behaviour:
macro_rules! iflet {
([$p:pat = $e:expr] $($rest:tt)*) => {
if let $p = $e {
iflet!($($rest)*);
}
};
($b:block) => {
$b
};
}
fn main() {
iflet!([Some(a) = foo_a()] [Some(b) = foo_b()] [Some(c) = foo_c()] {
println!("{} {} {}", a, b, c);
});
}
Playground

The standard library doesn't include that exact functionality, but the language allows you to create the desired behavior using a small macro.
Here's what I came up with:
macro_rules! all_or_nothing {
($($opt:expr),*) => {{
if false $(|| $opt.is_none())* {
None
} else {
Some(($($opt.unwrap(),)*))
}
}};
}
You can feed it all your options and get some tuple containing the unwrapped values if all values are Some, or None in the case that any of the options are None.
The following is a brief example on how to use it:
fn main() {
let foo = Some(0);
let bar = Some(1);
let baz = Some(2);
if let Some((a, b, c)) = all_or_nothing!(foo, bar, baz) {
println!("foo: {}; bar: {}; baz: {}", a, b, c);
} else {
panic!("Something was `None`!");
}
}
Here's a full test-suite for the macro: Rust Playground

My first inclination was to do something similar to swizard's answer, but to wrap it up in a trait to make the chaining cleaner. It's also a bit simpler without the need for extra function invocations.
It does have the downside of increasing the nesting of the tuples.
fn foo_a() -> Option<u8> {
println!("foo_a() invoked");
Some(1)
}
fn foo_b() -> Option<u8> {
println!("foo_b() invoked");
None
}
fn foo_c() -> Option<u8> {
println!("foo_c() invoked");
Some(3)
}
trait Thing<T> {
fn thing<F, U>(self, f: F) -> Option<(T, U)> where F: FnOnce() -> Option<U>;
}
impl<T> Thing<T> for Option<T> {
fn thing<F, U>(self, f: F) -> Option<(T, U)>
where F: FnOnce() -> Option<U>
{
self.and_then(|a| f().map(|b| (a, b)))
}
}
fn main() {
let x = foo_a()
.thing(foo_b)
.thing(foo_c);
match x {
Some(((a, b), c)) => println!("matched: a = {}, b = {}, c = {}", a, b, c),
None => println!("nothing matched"),
}
}

Honestly, someone should notice about Option being an applicative functor :)
The code will be quite ugly without currying support in Rust, but it works and it shouldn't make a noisy diff:
fn foo_a() -> Option<isize> {
println!("foo_a() invoked");
Some(1)
}
fn foo_b() -> Option<isize> {
println!("foo_b() invoked");
Some(2)
}
fn foo_c() -> Option<isize> {
println!("foo_c() invoked");
Some(3)
}
let x = Some(|v| v)
.and_then(|k| foo_a().map(|v| move |x| k((v, x))))
.and_then(|k| foo_b().map(|v| move |x| k((v, x))))
.and_then(|k| foo_c().map(|v| move |x| k((v, x))))
.map(|k| k(()));
match x {
Some((a, (b, (c, ())))) =>
println!("matched: a = {}, b = {}, c = {}", a, b, c),
None =>
println!("nothing matched"),
}

You can group the values using the '?' operator to return an Option of a tuple with the required values. If on of then is None, the group_options function will return None.
fn foo_a() -> Option<u8> {
println!("foo_a() invoked");
Some(1)
}
fn foo_b() -> Option<u8> {
println!("foo_b() invoked");
None
}
fn foo_c() -> Option<u8> {
println!("foo_c() invoked");
Some(3)
}
fn group_options() -> Option<(u8, u8, u8)> {
let a = foo_a()?;
let b = foo_b()?;
let c = foo_c()?;
Some((a, b, c))
}
fn main() {
if let Some((a, b, c)) = group_options() {
println!("{}", a);
println!("{}", b);
println!("{}", c);
}
}

Related

Is there an idiomatic way of avoiding `Box::leak` with this code?

As I continue to learn Rust I'm working on a project which involves extensive use of predicate functions. I've decided to implement these predicates with Rust closures, e.g.:
type Predicate = Box<Fn(&Form) -> bool>.
My program uses boolean logic applied to these predicates. For instance, both and as well as or are applied over the value of these predicates. I've made this work using Box::leak:
struct Form {
name: String,
}
fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
if a.is_none() {
return b;
} else if b.is_none() {
return a;
} else {
let a = Box::leak(a.unwrap());
let b = Box::leak(b.unwrap());
return Some(Box::new(move |form: &Form| a(form) && b(form)));
}
}
While this seems to work as I'd like, Box::leak seems non-ideal. I don't know enough about std::rc::Rc and std::cell::RefCell to know if these might help me avoid Box::leak here — employing them might require significant restructuring of my code, but I'd like to at least understand what the idiomatic approach here might be.
Is there a way of avoiding the leak while still maintaining the same functionality?
Here's the complete example:
struct Form {
name: String,
}
type Predicate = Box<Fn(&Form) -> bool>;
struct Foo {
predicates: Vec<Predicate>,
}
impl Foo {
fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
if a.is_none() {
return b;
} else if b.is_none() {
return a;
} else {
let a = Box::leak(a.unwrap());
let b = Box::leak(b.unwrap());
return Some(Box::new(move |form: &Form| a(form) && b(form)));
}
}
}
fn main() {
let pred = Foo::and(
Some(Box::new(move |form: &Form| {
form.name == String::from("bar")
})),
Some(Box::new(move |_: &Form| true)),
)
.unwrap();
let foo = Foo {
predicates: vec![pred],
};
let pred = &foo.predicates[0];
let form_a = &Form {
name: String::from("bar"),
};
let form_b = &Form {
name: String::from("baz"),
};
assert_eq!(pred(form_a), true);
assert_eq!(pred(form_b), false);
}
Your code does not need Box::leak and it's unclear why you think it does. The code continues to compile and have the same output if it's removed:
impl Foo {
fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
if a.is_none() {
b
} else if b.is_none() {
a
} else {
let a = a.unwrap();
let b = b.unwrap();
Some(Box::new(move |form: &Form| a(form) && b(form)))
}
}
}
The unwraps are non-idiomatic; a more idiomatic solution would use match:
impl Foo {
fn and(a: Option<Predicate>, b: Option<Predicate>) -> Option<Predicate> {
match (a, b) {
(a, None) => a,
(None, b) => b,
(Some(a), Some(b)) => Some(Box::new(move |form| a(form) && b(form))),
}
}
}

Call `stringify!` inside macro

This macro compiles when invoked:
macro_rules! remote_optional {
($remote:ident with=$def:ident $def_str:expr) => {
impl $def {
fn deserialize_option<'de, D>(deserializer: D) -> Result<Option<$remote>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(with = $def_str)] $remote);
let v: Option<Wrapper> = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}
}
}
}
This one doesn't:
macro_rules! remote_optional {
($remote:ident with=$def:ident) => {
impl $def {
fn deserialize_option<'de, D>(deserializer: D) -> Result<Option<$remote>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(with = stringify!($def))] $remote);
let v: Option<Wrapper> = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}
}
}
}
This is because stringify!($def) is passed into the #[serde(...)] attribute unevaluated.
Is there any practical workaround?
Could the macro of two arguments forward to the macro of three arguments, expanding the def identifier?
macro_rules! remote_optional {
// The one that doesn't work (two arguments)
// forwards to the one that *does* work, expanding the
// string.
($remote:ident with=$def:ident) => {
remote_optional!($remote, with=$def, stringify!($def));
};
// The macro that *does* work
($remote:ident with=$def:ident $def_str:expr) => {
impl $def {
fn deserialize_option<'de, D>(deserializer: D) -> Result<Option<$remote>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(with = $def_str)] $remote);
let v: Option<Wrapper> = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}
}
};
}
We could also consider making the macro of three arguments an implementation detail.
Little, isolated proof-of-concept:
macro_rules! my_macro {
($x:expr, $y:expr) => {
my_macro!($x, $y, stringify!($x + $y));
};
($x:expr, $y:expr, $msg:expr) => {
println!("{} + {} = {}", $x, $y, $msg);
};
}
fn main() {
my_macro!(3, 2); // 3 + 2 = 3 + 2
}

How do you unwrap a Result on Ok or return from the function on Err?

I have a function that calls another function which returns a Result. I need to check if the Result is Ok or Err and if it is an Err, I need to return early from my function. This is what I'm doing now:
match callable(&mut param) {
Ok(_v) => (),
Err(_e) => return,
};
Is there a more idiomatic Rust way to do this?
You can create a macro:
macro_rules! unwrap_or_return {
( $e:expr ) => {
match $e {
Ok(x) => x,
Err(_) => return,
}
}
}
fn callable(param: &mut i32) -> Result<i32, ()> {
Ok(*param)
}
fn main() {
let mut param = 0;
let res = unwrap_or_return!(callable(&mut param));
println!("{:?}", res);
}
Note that I wouldn't recommend discarding the errors. Rust's error handling is pretty ergonomic, so I would return the error, even if it is only to log it:
fn callable(param: &mut i32) -> Result<i32, ()> {
Ok(*param)
}
fn run() -> Result<(), ()> {
let mut param = 0;
let res = callable(&mut param)?;
println!("{:?}", res);
Ok(())
}
fn main() {
if let Err(()) = run() {
println!("Oops, something went wrong!");
}
}
If both functions return Result<doesn't matter, same T> you can just put a ? at the end of line of call.
fn caller() -> Result<Str, i32> {
let number = job()?; // <-- if job return error this function return/end here
// otherwise the value of Ok will assign to number
Ok(format!("the number is {}", number))
}
fn job() -> Result<i32, i32> {
// do something
Err(3)
}
You can use same pattern for Option<T> too.
You can use my unwrap_or crate to accomplish this.
You can do:
unwrap_or_ok!(callable(&mut param), _, return);
And if you want the result and to return the error, you can do:
let v = unwrap_or_ok!(callable(&mut param), error, return error);
Rust 1.65.0 has stabilized let-else statements, which enables you to write:
let Ok(_v) = callable(&mut param) else { return };

How to implement trim for Vec<u8>?

Rust provides a trim method for strings: str.trim() removing leading and trailing whitespace. I want to have a method that does the same for bytestrings. It should take a Vec<u8> and remove leading and trailing whitespace (space, 0x20 and htab, 0x09).
Writing a trim_left() is easy, you can just use an iterator with skip_while(): Rust Playground
fn main() {
let a: &[u8] = b" fo o ";
let b: Vec<u8> = a.iter().map(|x| x.clone()).skip_while(|x| x == &0x20 || x == &0x09).collect();
println!("{:?}", b);
}
But to trim the right characters I would need to look ahead if no other letter is in the list after whitespace was found.
Here's an implementation that returns a slice, rather than a new Vec<u8>, as str::trim() does. It's also implemented on [u8], since that's more general than Vec<u8> (you can obtain a slice from a vector cheaply, but creating a vector from a slice is more costly, since it involves a heap allocation and a copy).
trait SliceExt {
fn trim(&self) -> &Self;
}
impl SliceExt for [u8] {
fn trim(&self) -> &[u8] {
fn is_whitespace(c: &u8) -> bool {
*c == b'\t' || *c == b' '
}
fn is_not_whitespace(c: &u8) -> bool {
!is_whitespace(c)
}
if let Some(first) = self.iter().position(is_not_whitespace) {
if let Some(last) = self.iter().rposition(is_not_whitespace) {
&self[first..last + 1]
} else {
unreachable!();
}
} else {
&[]
}
}
}
fn main() {
let a = b" fo o ";
let b = a.trim();
println!("{:?}", b);
}
If you really need a Vec<u8> after the trim(), you can just call into() on the slice to turn it into a Vec<u8>.
fn main() {
let a = b" fo o ";
let b: Vec<u8> = a.trim().into();
println!("{:?}", b);
}
This is a much simpler version than the other answers.
pub fn trim_ascii_whitespace(x: &[u8]) -> &[u8] {
let from = match x.iter().position(|x| !x.is_ascii_whitespace()) {
Some(i) => i,
None => return &x[0..0],
};
let to = x.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
&x[from..=to]
}
Weird that this isn't in the standard library. I would have thought it was a common task.
Anyway here it is as a complete file/trait (with tests!) that you can copy/paste.
use std::ops::Deref;
/// Trait to allow trimming ascii whitespace from a &[u8].
pub trait TrimAsciiWhitespace {
/// Trim ascii whitespace (based on `is_ascii_whitespace()`) from the
/// start and end of a slice.
fn trim_ascii_whitespace(&self) -> &[u8];
}
impl<T: Deref<Target=[u8]>> TrimAsciiWhitespace for T {
fn trim_ascii_whitespace(&self) -> &[u8] {
let from = match self.iter().position(|x| !x.is_ascii_whitespace()) {
Some(i) => i,
None => return &self[0..0],
};
let to = self.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
&self[from..=to]
}
}
#[cfg(test)]
mod test {
use super::TrimAsciiWhitespace;
#[test]
fn basic_trimming() {
assert_eq!(b" A ".trim_ascii_whitespace(), b"A");
assert_eq!(b" AB ".trim_ascii_whitespace(), b"AB");
assert_eq!(b"A ".trim_ascii_whitespace(), b"A");
assert_eq!(b"AB ".trim_ascii_whitespace(), b"AB");
assert_eq!(b" A".trim_ascii_whitespace(), b"A");
assert_eq!(b" AB".trim_ascii_whitespace(), b"AB");
assert_eq!(b" A B ".trim_ascii_whitespace(), b"A B");
assert_eq!(b"A B ".trim_ascii_whitespace(), b"A B");
assert_eq!(b" A B".trim_ascii_whitespace(), b"A B");
assert_eq!(b" ".trim_ascii_whitespace(), b"");
assert_eq!(b" ".trim_ascii_whitespace(), b"");
}
}
All we have to do is find the index of the first non-whitespace character, one time counting forward from the start, and another time counting backwards from the end.
fn is_not_whitespace(e: &u8) -> bool {
*e != 0x20 && *e != 0x09
}
fn main() {
let a: &[u8] = b" fo o ";
// find the index of first non-whitespace char
let begin = a.iter()
.position(is_not_whitespace);
// find the index of the last non-whitespace char
let end = a.iter()
.rev()
.position(is_not_whitespace)
.map(|j| a.len() - j);
// build it
let vec = begin.and_then(|i| end.map(|j| a[i..j].iter().collect()))
.unwrap_or(Vec::new());
println!("{:?}", vec);
}

Is there an easy way to cast entire tuples of scalar values at once?

I want to cast a (u16, u16) to a (f32, f32). This is what I tried:
let tuple1 = (5u16, 8u16);
let tuple2 = tuple1 as (f32, f32);
Ideally, I would like to avoid writing
let tuple2 = (tuple1.0 as f32, tuple1.1 as f32);
There's no built-in way to do this, but one can do it with a macro:
macro_rules! tuple_as {
($t: expr, ($($ty: ident),*)) => {
{
let ($($ty,)*) = $t;
($($ty as $ty,)*)
}
}
}
fn main() {
let t: (u8, char, isize) = (97, 'a', -1);
let other = tuple_as!(t, (char, i32, i8));
println!("{:?}", other);
}
Prints ('a', 97, -1).
The macro only works for casting between types with names that are a single identifier (that's what the : ident refers to), since it reuses those names for binding to the elements of the source tuple to be able to cast them. All primitive types are valid single identifiers, so it works well for those.
No, you cannot. This is roughly equivalent to "can I cast all the fields in a struct to different types all at once?".
You can write a generic extension trait which can do this conversion for you, the only problem is that I don't believe there's any existing generic "conversion" trait which also has a u16 -> f32 implementation defined.
If you really want a function that does this, here is an as-minimal-as-I-could-make-it skeleton you can build on:
trait TupleCast<T> {
type Output;
fn tuple_cast(self) -> <Self as TupleCast<T>>::Output;
}
impl<T> TupleCast<T> for () {
type Output = ();
fn tuple_cast(self) -> <() as TupleCast<T>>::Output {
()
}
}
impl<S, T> TupleCast<T> for (S,) where S: CustomAs<T> {
type Output = (T,);
fn tuple_cast(self) -> <(S,) as TupleCast<T>>::Output {
(self.0.custom_as(),)
}
}
impl<S, T> TupleCast<T> for (S, S) where S: CustomAs<T> {
type Output = (T, T);
fn tuple_cast(self) -> <(S, S) as TupleCast<T>>::Output {
(self.0.custom_as(), self.1.custom_as())
}
}
// You would probably have more impls, up to some size limit.
// We can't use std::convert::From, because it isn't defined for the same
// basic types as the `as` operator is... which kinda sucks. So, we have
// to implement the desired conversions ourselves.
//
// Since this would be hideously tedious, we can use a macro to speed things
// up a little.
trait CustomAs<T> {
fn custom_as(self) -> T;
}
macro_rules! custom_as_impl {
($src:ty:) => {};
($src:ty: $dst:ty) => {
impl CustomAs<$dst> for $src {
fn custom_as(self) -> $dst {
self as $dst
}
}
};
($src:ty: $dst:ty, $($rest:ty),*) => {
custom_as_impl! { $src: $dst }
custom_as_impl! { $src: $($rest),* }
};
}
// You could obviously list others, or do manual impls.
custom_as_impl! { u16: u16, u32, u64, i32, i64, f32, f64 }
fn main() {
let x: (u16, u16) = (1, 2);
let y: (f32, f32) = x.tuple_cast();
println!("{:?}", y);
}
No,
there
is
not.
this version handles a few more cases Playground Example
original source: https://stackoverflow.com/a/29981602/5979634
because of matching rules, for single type casts just use as_tuple!(expr, T) or as_tuple!(expr, (T))
the rest works as in the original answer
macro_rules! tuple_as {
($t: expr, $ty: ident) => {{
let (a, b) = $t;
let a = a as $ty;
let b = b as $ty;
(a, b)
}};
($t: expr, ($ty: ident)) => {{
let (a, b) = $t;
let a = a as $ty;
let b = b as $ty;
(a, b)
}};
($t: expr, ($($ty: ident),*)) => {{
let ($($ty,)*) = $t;
($($ty as $ty,)*)
}}}

Resources