Method delegation with less boilerplate
This crate helps remove some boilerplate for structs that simply delegates most of its methods to one or more fields.
For example, let's say we want to implement a stack in Rust. In case you aren't familiar with the idea, a stack is a data structure in which items are inserted and accessed in a LIFO (Last-In, First-Out) manner. Typically, a stack supports the following basic operations:
- Push: insert an item to the top of the stack
- Pop: remove an item from the top of the stack (if stack is not empty)
In addition, a stack may support secondary operations such as querying if the stack is empty, the current size of the stack, a peek operation that gives access to the top item without removing it, a method to clear the stack and so on.
One way to implement such a data structure in Rust would be to use a Vec
:
As you can see, Vec
already supports most of the operations we needed, so in
most cases our implementation is simply delegating to the underlying Vec
,
except ocassionally re-mapping the method to a different name (e.g. peek()
is
called last()
in Vec
).
The fact that these implemenations are boring (simply delegating to another struct) is probably notable and worth calling out. If the reader of the code is already familiar with the behavior with the other struct, they can safely gloss over these methods and focus on the more interesting ones.Further more, if we can trust that the struct we are delegating to has a solid implementation and is well tested, we can probably just write a simple smoke test and not worry about re-testing the edge cases.
Unfortunately, this detail could easily get lost, especially when these methods are burried within other non-delegating methods. The only way to be sure is to carefully read the implementation to confirm that they aren't doing anything more, which somewhat defeats the purpose.
The delegate!
macro in this crate helps solve this problem by making your
delegating methods more declarative:
extern crate delegate;
This macro invocation would generate exactly the same codeas we had written by hand in the example above (with one minor difference, see below). Not only did you save a few lines of typing, you are making your intent more clear to your readers as well.
The macro support all the usual syntatic elements that are valid around method
declarations, such as (doc) comments, attributes, pub
modifiers, generics,
lifetimes, return type and where clauses. The only difference is that instead
of providing a block for the method body, you simply end it with a ;
after
the method signature. The macro will automatically generate a suitable body.
The macro will also automatically insert the #[inline]
hint for the compiler,
which is often desirable for delegating methods. You can override this by
inserting an explicit #[inline]
attribute (such as #[inline(always)]
or
#[inline(never)]
).
As seen in the example above, if the name of the method does not match, you can
override the inferred name (same name as your struct method) with the custom
#[target_method(...)]
attribute. (This attribute is removed by the macro
during expansion, so it does not rely on the experimental "custom_attribute"
feature.)
You may also delegate different methods to different fields inside the same
delegate!
block. For example:
extern crate delegate;
This macro is implemented completely using the macro_rules
system, therefore,
this crate does not have a dependency on the nightly compiler or any unstable
features. However, since the macro does recurse pretty deeply, you may need to
add the #![recursion_limit="..."]
attribute. The compiler will let you know
if/when it is necessary.
License
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Conduct
Please follow the Rust Code of Conduct. For escalation or moderation issues
please contact the crate author(s) listed in Cargo.toml
.