net_types

Macro map_ip_twice

Source
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)
        },
    )
}