macro_rules! mapper { ($($x:ident $(, $lt:lifetime)*: $it:ty $(=> $ot:ty)? : $e:expr);* $(;)?) => { ... }; ($($x:ident $(, $lt:lifetime)*: $it:ty $(=> $ot:ty)? : $e:expr ;)* $(_ $(=> $ot2:ty)? : $f:ident $(where $($t:tt)*)?)?) => { ... }; (@impl _ : $f:ident $(where $($t:tt)*)?) => { ... }; (@impl _ => $ot:ty : $f:ident $(where $($t:tt)*)?) => { ... }; (@impl $x:ident : $it:ty : $e:expr) => { ... }; (@impl $x:ident $(, $lt:lifetime)* : $it:ty : $e:expr) => { ... }; (@impl $x:ident : $it:ty => $ot:ty : $e:expr) => { ... }; (@impl $x:ident $(, $lt:lifetime)* : $it:ty => $ot:ty : $e:expr) => { ... }; }
Expand description
Provides a simple way to create a functor that implements Mapper.
§Syntax
Introduce = Variable [, Lifetime1, Lifetime2, ... ]
TypeMap = TypeIn [=> TypeOut]
Specialized = Introduce : TypeMap : Expr
Universal = _ : TypeMap : Function [where Bounds]
mapper!([ Specialized1; Specialized2; ... ] [; Univsersal])
The [ and ] markers only indicate optional content but not that the [ and ] need to be input.
Similarly, ... indicates several repeated segments, rather than inputing ....
§Explanation
§Specialized mapping rules
Firstly introduce a variable name used to represent the element (Recommand using x) and all possible lifetime paramters.
If the element type contains a reference or generic lifetime parameter, please explicitly annotate all lifetimes:
x, 'a
Then specify the mapping of the type, that is, element type => output type. If the element type and output type are the same type, the output type can be omitted:
&'a str => &'a [u8]
Next, write down the conversion expression, remember that x is an immutable reference to the element:
x.as_bytes()
Finally, use : to link them, and a specialized mapping rule is complete:
x, 'a : &'a str => &'a [u8] : x.as_bytes()
Construct specialized mapping rules for all element types in the tuple,
and then combine them in a mapper! macro to traverse the tuple:
use tuplez::*;
let tup = tuple!(1, "hello", 3.14).foreach(mapper!{
x: i32 : *x + 1; // Omit the output type
x: f32 => String: x.to_string();
x, 'a: &'a str => &'a [u8]: x.as_bytes()
});
assert_eq!(tup, tuple!(2, b"hello" as &[u8], "3.14".to_string()));§Universal mapping rule
Universal mapping rule currently only supports conversion through generic functions, not expressions. So first define the mapping function:
fn to_string<T: ToString>(v: &T) -> String {
v.to_string()
}Write down the mapping of types as before, only this time we use an _ for element types.
_ => String
Finally, use : to link them. But there is a little noise here. We need to write down the bounds of the mapping
function as well:
_ => String : to_string where ToString
Put it in the mapper! macro to traverse the tuple:
use tuplez::*;
fn to_string<T: ToString>(v: &T) -> String {
v.to_string()
}
let tup = tuple!(1, "hello", 3.14);
let tup2 = tup.foreach(mapper! {
_ => String: to_string where ToString
});
assert_eq!(
tup2,
tuple!("1".to_string(), "hello".to_string(), "3.14".to_string())
);If the mapping function output the same type, the output type can be omitted:
use tuplez::*;
fn just<T: Copy>(v: &T) -> T { *v }
let tup = tuple!(1, "hello", 3.14);
let tup2 = tup.foreach(mapper! {
_ : just where Copy
});
assert_eq!(tup, tup2);§Use both
You can use both multiple specialized mapping rules and one universal mapping rule in a mapper! macro,
but there are some restrictions.
- The universal mapping rule must be placed after all specialized mapping rules.
- Types that use specialized mapping rules must be exclusive from the bounds of the universal mapping rule.
- Either all types that use specialized mapping rules is your custom types, or the bounds of the universal mapping rule contain your custom traits.
Example of custom type:
use tuplez::*;
struct MyElement(i32);
fn to_string<T: ToString>(v: &T) -> String {
v.to_string()
}
let tup = tuple!(MyElement(12), "hello", 3.14);
let tup2 = tup.foreach(mapper! {
x : MyElement => i32 : x.0;
_ => String: to_string where ToString
});
assert_eq!(tup2, tuple!(12, "hello".to_string(), "3.14".to_string()));Example of custom trait:
use tuplez::*;
trait MyToString: ToString {}
impl MyToString for &str {}
impl MyToString for f32 {}
fn to_string<T: MyToString>(v: &T) -> String {
v.to_string()
}
let tup = tuple!(vec![12, 14], "hello", 3.14);
/* Using `ToString` here is not allowed because
* neither `Vec` nor `ToString` is defined in current crate,
* even though `Vec` does not implement `ToString`.
*/
let tup2 = tup.foreach(mapper! {
x : Vec<i32> => i32 : x[0];
_ => String: to_string where MyToString
});
assert_eq!(tup2, tuple!(12, "hello".to_string(), "3.14".to_string()));