mapper

Macro mapper 

Source
macro_rules! mapper {
    ($($t:tt)*) => { ... };
}
Expand description

Provides a simple way to build a mapper that implements Mapper.

§Syntax

Generic = [Lifetime1, Lifetime2, ...] [Type1 [: Bound1], Type2 [: Bound2], ...]
Rule    = [ < Generic > ] | Variable : InputType | [-> OutputType] { Body } [,] [;]

mapper!( [Rule1 Rule2 ... ] )

[ and ] only indicate the optional content but not that they need to be input.

Similarly, ... indicates several repeated segments, rather than inputting ....

§Explanation

A mapping rule is much like a closure, except that it must annotate the argument type and the return type:

|x: i32| -> i64 { x as i64 }

Note that it’s just like but not really a closure, so you can’t capture context variables.

Also supports adding mut:

|mut x: i32| -> i64 { x += 1; x as i64 }

If the return value requires a lifetime, you need to explicitly introduce the lifetime annotation, since Rust binds the lifetime of return value to the mapper instead of the element by default:

<'a> |x: &'a str| -> &'a [u8] { x.as_bytes() }

You can omit the return type when the return type is the same as the element type. Note: Do not misunderstand that the return type is automatically inferred or is a ().

|x: i32| { x + 1 }

You can also introduce generic types like this:

<T> |x: Option<T>| -> T { x.unwrap() }

Many times, you may also need to add bounds to the generic type:

<T: ToString> |x: Option<T>| -> String { x.unwrap().to_string() }

Construct mapping rules for all element types in the tuple, and then combine them in the mapper! macro to traverse the tuple:

use tuplez::{mapper, tuple, TupleLike};

let tup = tuple!(1, "hello", Some(3.14)).foreach(mapper! {
    |mut x: i32| -> i64 { x += 1; x as i64 }
    <T: ToString> |x: Option<T>| -> String { x.unwrap().to_string() }
    <'a> |x: &'a str| -> &'a [u8] { x.as_bytes() }
});
assert_eq!(tup, tuple!(2i64, b"hello" as &[u8], "3.14".to_string()));

It is allowed to add commas or semicolons as separators between rules. Sometimes this may look better.

Tip: If you don’t want to consume the tuple, call its as_ref() before traversing. Likewise, if you want to modify elements of tuple, call its as_mut() before traversing.

use tuplez::{mapper, tuple, TupleLike};

let mut tup = tuple!(1, "hello", Some(3.14));
let tup2 = tup.as_ref().foreach(mapper! {
    |x: &i32| -> i32 { *x + 1 }
    <T: ToString> |x: &Option<T>| -> String { x.as_ref().unwrap().to_string() }
    <'a> |x: &&'a str| -> &'a [u8] { x.as_bytes() }
});
assert_eq!(tup2, tuple!(2, b"hello" as &[u8], "3.14".to_string()));
assert_eq!(tup, tuple!(1, "hello", Some(3.14)));  // And the original tuple is not consumed

_ = tup.as_mut().foreach(mapper! {
    |x: &mut i32| -> () { *x += 1; }
    <T: ToString> |x: &mut Option<T>| -> () { x.take(); }
    |x: &mut &str| -> () { *x = "world" }
});
assert_eq!(tup, tuple!(2, "world", None));