Boilerplate for building visitors, inspired by
derive-visitor.
Driving a visitor
The premise of this crate is that to visit a type means to call a function on each of its
fields. The Visit[Mut] and Drive[Mut] traits of this module provide the simplest interface
for this: a type that implements a Visit<...> for a bunch of types is like a bundle of
FnMut closures, and drive_inner on a type T calls <V as Visit<FieldTy>>::visit on each
field of T.
The Drive/DriveMut derive macros implement these types automatically for a type. With that
boilerplate out of the way, it becomes easy to define flexible visitors.
The output of the derive macros looks like:
As you can see, this is not recursive in any way: x.drive_inner(v) simply calls v.visit()
on each field of x; it is up to the visitor to recurse into nested structures if it wishes.
Defining useful visitors
A visitor is a type that implements Visit<T>/VisitMut<T> for a set of types T. An
implementation of Visit[Mut] typically involves calling x.drive_inner(self) to recurse into
the type's contents, with some work done before or after that call. The Visit and VisitMut
derive macros make such usage straightforward.
// recurse without custom behavior
// recurse without custom behavior
// call `self.enter_my_node` before recursing
// do nothing on a string
;
/// Concatenate all the strings in this list.
This expands to:
;
// Recurse without custom behavior
// Recurse without custom behavior
// Call `self.enter_my_node` before recursing
// Do nothing on a string
The options available are:
enter(Ty): callself.enter_ty(x)before recursing withdrive_inner.exit(Ty): callself.exit_ty(x)after recursing withdrive_inner.override(Ty): callself.visit_ty(x)?, which may or may not recurse if it wishes to.drive(Ty): recurse withdrive_inner.skip(Ty): do nothing.Ty: alias foroverride(Ty)
Instead of Ty, one can always write for<A, B, C> Ty<A, B, C> to make a generic impl. For
enter, exit and override, one may also write name: Ty so that visit_name etc is
called instead of visit_ty.
Reusable visitors
For more complex scenarios where one-off visitor structs would be tedious, this crate provides
a final macro: visitable_group. Given a set of types of interest, this generates a pair of
traits: a Visitable trait implemented by all these types, and a Visitor trait with default
methods that defines visitors over these types.
This is a reusable version of the one-off visitor structs we saw in the previous section: the
enter_foo/exit_foo/visit_foo methods are now trait methods, in such a way that many
visitors can be defined for that same set of types.
;
The generated visitor trait has methods much like those from the Visit[Mut] derives, that can
be overriden freely. The result is:
/// Implementation detail: wrapper that implements `Visit[Mut]<T>` for `T: ListVisitable`,
/// and delegates all the visiting to our trait's `drive[_mut]`. Used in the implementation of
/// `visit_inner`
;
To illustrate, the typical visit loop would look like, given a MyVisitor: ListVisitor:
<MyVisitor as ListVisitor>::visit(v, x)<Node as GroupVisitable>::drive_list(x, v)// assumingx: Node<MyVisitor as ListVisitor>::visit_node(v, x)// here lives custom behavior<MyVisitor as ListVisitor>::visit_inner(v, x)<Node as Drive>::drive_inner(ListVisitorWrapper(v))- calls
<MyVisitor as ListVisitor>::visit(v, &x.field)on each field ofx
The options available for the visitable_group macro are:
visitor(drive_method_name(&[mut]TraitName)[, infaillible]): derive a visitor trait namedTraitName.- the presence of
mutdetermines whether theTraitNamevisitor will operate on mutable or immutable borrows. - the optional
infaillibleflag enables an infaillible-style interface for the visitor:, where its methodsvisit_$tyreturn()instead ofControlFlow<_>.
- the presence of
override_skip(Ty): similar tooverride(Ty), but the default implementation does nothing, and noenter_Tyorexit_Tymethods are generated.override(Ty),drive(Ty)andskip(Ty): behave the same as their counterparts in theVisitandVisitMutderives.
Note: the visitable_group interface makes it possible to write composable
visitor wrappers that provide reusable functionality. For an example, see
[derive_generic_visitor/tests/visitable_group_wrapper.rs].