struct-split
Efficiently split struct fields into distinct subsets of references, ensuring zero overhead and strict borrow checker compliance (non-overlapping mutable references).
Example Usage
Suppose we’re building a rendering engine with registries for geometry, material, and scenes. Entities reference each other by ID (usize), stored within various registries:
use Split;
With struct-split
, you can separate Ctx
into subsets of field references:
// Take immutable reference to `mesh` and mutable references to both `geometry`
// and `material`.
#[module(...)]
Attribute
In the example above, we used the #[module(...)]
attribute, which specifies the path to the module where the macro is invoked. This attribute is necessary because, as of now, Rust does not allow procedural macros to automatically detect the path of the module they are used in. This limitation applies to both stable and unstable Rust versions.
If you intend to use the generated macro from another crate, avoid using the crate::
prefix in the #[module(...)]
attribute. Instead, refer to your current crate by its name, for example: #[module(my_crate::data)]
. However, Rust does not permit referring to the current crate by name by default. To enable this, add the following line to your lib.rs
file:
extern crate self as my_crate;
Generated Macro Syntax
A macro with the same name as the target struct is generated, allowing flexible reference specifications. The syntax follows these rules:
- Lifetime: The first argument can be an optional lifetime, which will be used for all references. If no lifetime is provided, '_ is used as the default.
- Mutability: Each field name can be prefixed with mut for a mutable reference or ref for an immutable reference. If no prefix is specified, the reference is immutable by default.
- Symbols:
*
can be used to include all fields.!
can be used to exclude a field (providing neither an immutable nor mutable reference).
- Override Capability: Symbols can override previous specifications, allowing flexible configurations. For example,
Ctx![mut *, geometry, !scene]
will provide a mutable reference to all fields exceptgeometry
andscene
, with geometry having an immutable reference and scene being completely inaccessible.
How it works under the hood
This macro performs a set of straightforward transformations. Consider the struct from the example above:
The macro defines a CtxRef
struct:
The Value
type adapts to either a reference, a mutable reference, or an inaccessible private mutable pointer, based on parameterization:
;
pub type Value<'t, L, T> = Value;
The macro generates as_ref_mut
and as_refs
methods for flexible reference creation:
The CtxRef struct provides fit
, fit_rest
, and split
methods:
An extract_$field
method is generated for each field:
Finally, the macro generates the Ctx!
macro itself.
Limitations
Currently, the macro works only with non-parametrized structures. For parameterized structures, please create an issue or submit a PR.