# A single derive macro for all your recursive traits This proc-macro trait introduces a special `derive` macro called `Recursive` that can be used to auto-generate a recursive implementation of most traits you'll be writing. ## Using `Recursive` ### with structs Let's look at the following trait: ```rust trait Count { /// Sums up all integer values in the data. fn count(&self) -> i32; } impl Count for Vec { fn count(&self) -> i32 { self.iter().map(T::count).sum() } } impl Count for i32 { fn count(&self) -> i32 { *self } } ``` Here, the `count` function should sum up all `i32` values in a piece of data. Suppose we want to implement it for the following struct: ```rust struct Foo { bar: i32, zee: Vec } ``` First of all, we have to derive `Recursive` for our type (and import the macro): ```rust use derive_recursive::Recursive; // ... #[derive(Recursive)] struct Foo { bar: i32, zee: Vec } ``` But this alone won't give us anything. In order to actually generate some code, we need to tell the library what traits to implement and how to implement their functions. We do this using the `recursive` helper macro: ```rust #[derive(Recursive)] #[recursive( impl Count for Self { fn count(&self) -> i32 { aggregate = + } } )] struct Foo { bar: i32, zee: Vec } ``` That's a lot of code, but, as you'll see, it's all really simple. First, we specify which trait we want to implement through a standard impl block. Note that it really is a standard impl block, as it also accepts generics the same way. That is, should the trait or the self-type have any generics, you'd write them as though you were actually implementing the trait. The only difference from a standard impl block is the forced usage of `Self`, rather than a path. `Self` here is only a substitute for the type's name, not the generics. Any generics must actually be given. When we're done specifying the trait, we specify what functions we want to implement. We have to write the full signatures of all required (and maybe optional) functions, as `derive_recursive` doesn't know anything about the traits it implements. The signature is written exactly as if it were in an actual impl block. Then, where normally the function's code would be, we put implementation parameters. Implementation parameters are how we specify what exactly should be done with the function. Here, we used `aggregate` to tell `Recursive` to merge the results of function calls on `Foo`'s fields with the `+` operator. The `aggregate` parameter also accepts `*`, `&`, `|`, `&&`, `||` as operators, `_` as a special `unit` aggregate, that only returns the result of the last recursive call, and a function path for custom aggregation. This works as expected with associated functions (field types). The `aggregate` parameter can also become `{}` denoting a constructor. The call results are then put together into a `Self`-typed return value. If no `aggregate` parameter is given, only the first field in the struct is processed. It's also worth noting, that any parameters of the recursive function are passed on to the next calls. Ultimately, it all expands into the following ```rust impl Count for Foo { fn count(&self) -> i32 { let Self { bar: f0, zee: f1 } = self; { ::count(f0) + as Count>::count(f1) } } } ``` Should you want to implement another trait for `Foo` this way, you need a new `recursive` helper macro. The below example shows a few concepts mentioned above: ```rust #[derive(Recursive)] #[recursive( impl Count for Self { fn count(&self) -> i32 { aggregate = + } } )] #[recursive( impl Bar for Self { fn bar(x: C) -> Self { aggregate = {} } } )] struct Foo { bar: C, zee: Vec } ``` This expands into ```rust impl Count for Foo { fn count(&self) -> i32 { let Self { bar: f0, zee: f1 } = self; { ::count(f0) + as Count>::count(f1) } } } impl Bar for Foo { fn bar(arg0: C) -> Self { Self { bar: >::bar(arg0), zee: as Bar>::bar(arg0), } } } ``` Fields affected by the implemented function can also be filtered with the `marker` attribute: ```rust #[derive(Recursive)] #[recursive( impl Bar for Self { fn modify(&mut self) { aggregate = _, marker = only_this } } )] struct Foo { a: String, b: u32, c: Vec<&'static str> } ``` Then we can mark the right fields with `#[recursive()]`: ```rust struct Foo { #[recursive(only_this)] a: String, b: u32, #[recursive(only_this)] c: Vec<&'static str> } ``` This expands into the following: ```rust impl Bar for Foo { fn modify(&mut self) { let Self { a: f0, b: f1, c: f2 } = self; { ::modify(f0); as Bar>::modify(f2) } } } ``` If aggregate was not present, the first field found with the right marker would be affected. You can add multiple markers to the same field, of course. It's worth mentioning that if the aggregate is `{}`, the `marker` attribute is invalid. Construction must affect all fields. Another useful attribute is the `wrap` attribute. This can be given two different values: `Result` and `Option`. If the attribute is present, all recursive function calls are followed by a `?` and the final returned value is wrapped into the respective variant (`Ok` or `Some`). ### with enums `recursive_derives` can also work with enums. All attributes mentioned above still apply and `marker` along with `aggregate` modify how variants are processed. For interactions (aggregation and filtering) with the enum's variants, we use `variant_aggregate` and `variant_marker`. `variant_aggregate` is only allowed with non-constructive (`attribute` is not `{}`) associated functions, as there is no aggregation possible when the function gets a receiver. The attribute decides how results produced by recursively applying the functions on the variants are aggregated. It accepts any values the `aggregate` attribute accepts, EXCEPT `{}`. `variant_marker` can be used to filter affected variants. It's only allowed on associated functions and REQUIRED on associated constructive functions (`aggregate` is `{}`). It works exactly the same way the `marker` attribute does, except this time, we apply attributes to the variants themselves. The below example presents deriving traits such as `Default` and `Clone` from the standard library and our own `Size` trait, determining the memory size of the implementor: ```rust #[derive(Recursive)] #[recursive( impl Default for Self { fn default() -> Self { aggregate = {}, variant_marker = default } } )] #[recursive( impl Clone for Self { fn clone(&self) -> Self { aggregate = {} } } )] #[recursive( impl Size for Self { fn size() -> usize { aggregate = +, variant_aggregate = max, } } )] enum Foo { #[recursive(default)] Bar(String, usize), Vee { a: Zee, b: Vec } } ``` This expands into ```rust impl Default for Foo { fn default() -> Self { Self::Bar(::default(), ::default()) } } impl Clone for Foo { fn clone(&self) -> Self { match self { Self::Bar(f0, f1) => { Self::Bar(::clone(f0), ::clone(f1)) } Self::Vee { a: f0, b: f1 } => { Self::Vee { a: ::clone(f0), b: as Clone>::clone(f1), } } } } } impl Size for Foo { fn size() -> usize { max( { ::size() + ::size() }, { ::size() + as Size>::size() }, ) } } ``` ## Additional attributes ### `init` and `variant_init` You can use the `init` attribute (accepts an expression) to add a special *init* expression that will be aggregated with subsequent recursive calls. The init expression is always the first executed one. You can achieve a similar result when aggregating enum variants using `variant_init`. ### `override_marker` The `override_marker` attribute can be used to add a custom function to call in place of a recursive call for a specific variant/field. The same attribute can be used for both. Variants and fields should be marked, like with other marker attributes, with one change: ```rust #[recursive(marker_name = func_here)] struct Foo; ``` The right hand side of the statement should be an expression returning a function (which means it also allows closures). ## Disclaimer Note, that this library quite fresh, has not been very tested and is limited in some ways (most notably, it has very limited support for unit structs and variants). However, it should be good enough for most use cases. Should you discover a bug or stumble upon an idea on how to improve the library (including things you need, but the library does not provide), feel free to open an issue (and maybe make the change yourself and add a PR, if you have the time). Note that I won't be working on the library a lot, since I have other, more important projects.