Anyone else use macros to make automatic trait impls for wrapper types?

Petgraph uses this “wonderful” homebrew technique of carbon-copying its trait definitions so that they can be used for automatic trait impls — it only works for wrapper types, but the crate has a lot of them. For example newtype wrappers like Frozen<G> or Reversed<G> where G would be any graph.

I'm just curious and would think it's fun to share and describe the trait impl templating madness that petgraph has invented.

The trait definition looks like this - it is a normal definition inside trait_template! { } with some markups: source link

trait_template! {
/// Access to the neighbors of each node, through incoming or outgoing edges.
pub trait IntoNeighborsDirected : IntoNeighbors {
    @section type
    type NeighborsDirected: Iterator<Item=Self::NodeId>;
    @section self
    fn neighbors_directed(self, n: Self::NodeId, d: Direction)
        -> Self::NeighborsDirected;
}
}

And the application of the "auto" trait impls look like this: (source link)

Data!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
DataMap!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
DataMapMut!{delegate_impl [['a, G], G, Frozen<'a, G>, access0]}
GetAdjacencyMatrix!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
IntoEdgeReferences!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoEdges!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoEdgesDirected!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoNeighbors!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoNeighborsDirected!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoNodeIdentifiers!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
IntoNodeReferences!{delegate_impl [['a, 'b, G], G, &'b Frozen<'a, G>, deref_twice]}
NodeCompactIndexable!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
NodeCount!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
NodeIndexable!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
GraphProp!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}
Visitable!{delegate_impl [['a, G], G, Frozen<'a, G>, deref_twice]}

You can see why we do this - that's 16 trait impls for this type that all delegate to the type inside the newtype wrapper, and this is not the only place it is used.

The implementation of trait_template is here and it is by no means a beauty.

So, maybe this could be used in more places, even if it's not so beautiful? It was done long before proc macros were stable, at least. :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.