A procedural attribute macro to reduce boilerplate when passing partially borrow structs into functions.
Overview
The #[clasma(..)] function annotation generates a macro_rules! macro of the same name and does not touch the function signature. This macro is callable similarly to the original function, except that one can pass a struct instance to provide the arguments corresponding to its fields.
This attempts to make partial or "split" borrows more ergonomic in Rust, where the borrow checker sometimes requires functions to borrow references to individual fields rather than a single mutable reference to the parent struct, leading to verbose call sites.
Usage
Consider the following struct:
let mut mystruct = Mystruct ;
By taking fields of Mystruct as arguments, the #[clasma(..)] attribute on a function foo will generate a macro foo!. Due to proc-macros being unable to resolve a struct identifier's field names, one must provide all field names of Mystruct to #[clasma(..)].
The first argument to the foo! is an instance of Mystruct. foo! borrows each of mystruct's fields individually that match foo's argument names. One passes the remaining arguments in the same order as the signature.
// Must list *all* field names of `Mystruct`
foo!;
// expands to:
// foo(&mut mystruct.a, &mystruct.b, 3);
One can reorder arguments; they just have to match with the struct's fields. One can also optionally provide generic parameters inside angle brackets <...>.
foo!;
// expands to:
// foo::<&str>(&mystruct.b, "hello", &mut mystruct.a, &mystruct.c);
Lifetime parameters are also supported.
const mystruct: Mystruct = Mystruct ;
foo!;
// expands to:
// foo::<'static>(&mystruct.b, &mystruct.a, &3);
Propagating from scope
Two functions marked with identical #[clasma(..)] attributes can call one another using <function name>_scope! macros. One calls foo_scope! identically to foo!, except that one omits the mystruct. The arguments to foo previously supplied by mystruct now come directly from the local scope at the call site.
impl-blocks
For impl blocks, the #[clasma(..)] attribute goes on top of the impl. This will generate macros for all functions in the block.
foo!;
// expands to:
// Mystruct::foo(&mut mystruct.a, &mystruct.b, 3);
If both the type and the function in the impl block are generic, one can provide the arguments separated by :: like so:
foo!;
// expands to:
// Mystruct::<&str>::foo::<u8>(&mut mystruct.a, &mystruct.b, 3, "hello");
If only the type is generic, one passes the arguments like this: foo!(<T>::, mystruct, ...)
Motivating Example
Imagine a Tourist with a list of travel destinations, that counts the number of times he visits one of his destinations.
Before
Without partial borrowing, this code does not compile.
The issue is that visit unnecessarily borrows self.destinations mutably when it only needs an immutable borrow. A workaround would be to borrow each struct field separately on every call to visit:
However, this gets tedious as the number of fields increases.
With clasma::partial
The partial macro handles borrowing and argument passing.
Installation