Skip to main content

fieldwork_borrow/
lib.rs

1//! Field-level borrow splitting for Rust.
2//!
3//! Rust's borrow checker operates at the function boundary level, which sometimes
4//! rejects code that is actually safe. This crate provides the [`with_!`] macro
5//! that enables field-level borrow splitting, allowing you to work around these
6//! "false positive" borrow checker errors.
7//!
8//! # The Problem
9//!
10//! Consider this common pattern that Rust rejects:
11//!
12//! ```compile_fail
13//! struct Database { models: Vec<Model> }
14//! struct Model { objects: Vec<Object> }
15//! struct Object { matrix: f64 }
16//!
17//! struct Viewer {
18//!     db: Database,
19//!     model_id: usize,
20//!     object_mats: Vec<f64>,
21//! }
22//!
23//! impl Viewer {
24//!     fn cur_model(&self) -> &Model {
25//!         &self.db.models[self.model_id]
26//!     }
27//!
28//!     fn problem(&mut self) {
29//!         // ERROR: cannot borrow `self.object_mats` as mutable because
30//!         // `self` is also borrowed as immutable (by cur_model)
31//!         for obj in &self.cur_model().objects {
32//!             self.object_mats.push(obj.matrix);
33//!         }
34//!     }
35//! }
36//! ```
37//!
38//! The code is safe because `cur_model()` only reads `db` and `model_id`,
39//! while we're mutating `object_mats`. But Rust can't see through the method boundary.
40//!
41//! # The Solution
42//!
43//! ```
44//! use fieldwork_borrow::with_;
45//!
46//! struct Database { models: Vec<Model> }
47//! struct Model { objects: Vec<Object> }
48//! struct Object { matrix: f64 }
49//!
50//! struct Viewer {
51//!     db: Database,
52//!     model_id: usize,
53//!     object_mats: Vec<f64>,
54//! }
55//!
56//! impl Viewer {
57//!     fn problem(&mut self) {
58//!         with_!(self, {
59//!             methods {
60//!                 cur_model(&db, copy model_id) -> &Model {
61//!                     &db.models[model_id]
62//!                 }
63//!             }
64//!             fields {
65//!                 mut object_mats
66//!             }
67//!             body {
68//!                 for obj in &self.cur_model().objects {
69//!                     object_mats.push(obj.matrix);
70//!                 }
71//!             }
72//!         });
73//!     }
74//! }
75//! ```
76//!
77//! # Syntax
78//!
79//! ```text
80//! with_!(receiver, {
81//!     methods {
82//!         method_name(field1, mut field2, copy field3, arg: Type) -> ReturnType {
83//!             // method body
84//!         }
85//!     }
86//!     fields {
87//!         field_a           // immutable borrow
88//!         mut field_b       // mutable borrow
89//!     }
90//!     body {
91//!         // code that uses self.method_name() and self.field_x
92//!     }
93//! });
94//! ```
95//!
96//! # Field Access Modes
97//!
98//! In method parameters:
99//! - `field` or `&field` — shared reference (`&receiver.field`)
100//! - `mut field` — mutable reference (`&mut receiver.field`)
101//! - `copy field` — by value, requires `Copy` trait
102//!
103//! In `fields` block:
104//! - `field` — shared reference
105//! - `mut field` — mutable reference
106
107pub use fieldwork_macro::with_;