macro_rules! map_ip_twice { ($ip:ident, $input:expr, $fn:expr $(,)?) => { ... }; ($ip:ty, $input:expr, $fn:expr $(,)?) => { ... }; ($ip:ty as $iptypealias:ident, $input:expr, $fn:expr $(,)?) => { ... }; }
Expand description
Invokes I::map_ip
, passing the same function body as both arguments.
The first argument is always the I
on which to invoke I::map_ip
.
Optionally, this can include an alias (I as IpAlias
) that should be bound
to Ipv4
and Ipv6
for each instantiation of the function body. (If the
Ip
argument passed is a simple identifier, then it is automatically
aliased in this way.)
The next argument is the input to thread through map_ip
to the function,
and the final argument is the function to be duplicated to serve as the
closures passed to map_ip
.
This macro helps avoid code duplication when working with types that are
not GenericOverIp, but have identical shapes such that the actual text of
the code you are writing is identical. This should be very rare, and is
generally limited to cases where we are interfacing with code that we don’t
have the ability to make generic-over-IP – when possible, it’s better to
push I: Ip
generics further through the types you are working with instead
so that you can avoid using map_ip
entirely.
Example:
// Imagine that `IpExt` is implemented for concrete `Ipv4` and `Ipv6` but
// not for blanket `I: Ip`.
struct Foo<I: IpExt>;
struct FooFactory;
impl FooFactory {
fn get<I: IpExt>(&self) -> Foo<I> {
unimplemented!()
}
}
struct FooSink<I: IpExt>;
impl<I: IpExt> FooSink<I> {
fn use_foo(&self, foo: Foo<I>) {
unimplemented!()
}
}
fn do_something<I: Ip>(factory: FooFactory) -> Foo<I> {
map_ip_twice!(
I,
(),
|()| {
// This works because even though the `I` from the function decl
// doesn't have an `IpExt` bound, it's aliased to either `Ipv4`
// or `Ipv6` here.
factory.get::<I>()
},
)
}
fn do_something_else<I: IpExt>(factory: FooFactory, foo_sink: FooSink<I>) {
map_ip_twice!(
// Introduce different alias to avoid shadowing `I`.
I as IpAlias,
(),
|()| {
let foo_with_orig_ip = factory.get::<I>();
// The fact that `I` was not shadowed allows us to make use of
// `foo_sink` by capture rather than needing to thread it
// through the generic-over-IP input.
foo_sink.use_foo(foo_with_orig_ip)
},
)
}