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