Ctx),
mesh: usize
) {
// ...
}
```
# 🔋 Batteries Included
Consider the following struct to demonstrate the key tools provided by the macro:
```rust
#[derive(PartialBorrow)]
#[module(crate::data)] // Current module, see explanation below.
pub struct Ctx {
pub geometry: GeometryCtx,
pub material: MaterialCtx,
pub mesh: MeshCtx,
pub scene: SceneCtx,
// Possibly many more fields...
}
```
The `Ctx` struct is equipped with the following methods:
```rust
impl Ctx {
/// Borrows all fields. The target type needs to be known,
/// e.g., `ctx.as_refs:: Ctx)>()`.
pub fn as_refs(&mut self) -> Target {
// ...
}
/// Borrows all fields mutably.
pub fn as_refs_mut(&mut self) -> p!( Ctx) {
// ...
}
}
```
The partially borrowed struct provides borrowing and splitting capabilities:
```rust
impl p!(* ... */>Ctx) {
/// Borrows required fields. The target type needs to be known,
/// e.g., `ctx.partial_borrow:: Ctx)>()`.
fn partial_borrow(&mut self) -> &mut Target {
// ...
}
/// Borrows fields required by `Target` and returns borrows of
/// all remaining fields. Please note, that if `Target` requires
/// an immutable borrow of a field, the remaining fields will also
/// include an immutable borrow of that field.
fn partial_borrow_rest(&mut self) ->
&mut >::Rest {
// ...
}
/// Borrows fields required by `Target` and returns borrows of
/// all remaining fields. Please refer to the `partial_borrow` and
/// `partial_borrow_rest` methods for more details.
fn split(&mut self) -> (
&mut Target,
&mut >::Rest
) {
// ...
}
// Extract the `geometry` field and return it along with the rest
// of the borrowed fields.
pub fn extract_geometry(&mut self) -> (
&mut GeometryCtx,
&mut Ctx)>>::Rest
) {
// ...
}
// Other `extract_$field` methods are generated similarly.
}
```
The partially borrowed struct also provides methods for concatenating partial borrows:
```rust
impl p!(* ... */>Ctx) {
/// Concatenates the current partial borrow with
/// another one.
fn union(&mut self, other: &mut Other)
-> Union {
// ...
}
}
/// The `Union` type is particularly useful when defining
/// type aliases.
type RenderCtx<'t> = p!(<'t, scene> Ctx);
type GlyphCtx<'t> = p!(<'t, geometry, material, mesh> Ctx);
type GlyphRenderCtx<'t> = Union, GlyphCtx<'t>>;
```
Please note, that while the `union` operation might seem useful, in most cases it is better to re-structure your code to avoid it. For example, let's consider the previous implementation of `render_pass1`:
```rust
fn render_pass1(ctx: p!(& Ctx)) {
let (scene, ctx2) = ctx.extract_scene();
for scene in &scene.data {
for mesh in &scene.meshes {
render_scene(ctx2.partial_borrow(), *mesh)
}
}
render_pass2(ctx);
}
```
It could also be implemented using explicit split and union, but it would make the code less readable:
```rust
fn render_pass1(ctx: p!(& Ctx)) {
// The `ctx` var shadows the original one here.
let (scene_ctx, ctx) = ctx.split:: Ctx)>();
for scene in &scene_ctx.scene.data {
for mesh in &scene.meshes {
render_scene(ctx.partial_borrow(), *mesh)
}
}
// Because the original var is inaccessible, we need to
// unify the parts back together.
let mut ctx = ctx.union(&scene_ctx);
render_pass2(&mut ctx);
}
```
# 👓 `#[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, currently, 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:
```rust
extern crate self as my_crate;
```
# 🛠 How It Works Under the Hood
This macro performs straightforward transformations. Consider the `Ctx` struct from the example above:
```rust
#[derive(Debug, Default, PartialBorrow)]
#[module(crate::data)]
pub struct Ctx {
pub geometry: GeometryCtx,
pub material: MaterialCtx,
pub mesh: MeshCtx,
pub scene: SceneCtx,
}
```
The macro defines a `CtxRef` struct:
```rust
#[repr(C)]
pub struct CtxRef {
geometry: Geometry,
material: Material,
mesh: Mesh,
scene: Scene,
}
```
Each type parameter is instantiated with one of `&`, `&mut`, or `Hidden`, a type used to safely hide fields that are not part of the current borrow:
```rust
#[repr(transparent)]
#[derive(Debug)]
pub struct Hidden(*mut T);
```
The `partial_borrow`, `partial_borrow_rest`, and `split` methods are implemented using inlined pointer casts, with safety guarantees enforced by the type system:
```rust
pub trait PartialBorrow {
type Rest;
#[inline(always)]
fn partial_borrow_impl(&mut self) -> &mut Target {
unsafe { &mut *(self as *mut _ as *mut _) }
}
#[inline(always)]
fn partial_borrow_rest_impl(&mut self) -> &mut Self:: Rest {
unsafe { &mut *(self as *mut _ as *mut _) }
}
#[inline(always)]
fn split_impl(&mut self) -> (&mut Target, &mut Self::Rest) {
let a = unsafe { &mut *(self as *mut _ as *mut _) };
let b = unsafe { &mut *(self as *mut _ as *mut _) };
(a, b)
}
}
```
Finally, a helper macro with the same name as the struct is generated and is used by the `partial_borrow` macro.
# ⚠️ Limitations
Currently, the macro works only with non-parametrized structures. For parametrized structures, please create an issue or submit a pull request.