Expand description
Field-level borrow splitting for Rust.
Rust’s borrow checker operates at the function boundary level, which sometimes
rejects code that is actually safe. This crate provides the with_! macro
that enables field-level borrow splitting, allowing you to work around these
“false positive” borrow checker errors.
§The Problem
Consider this common pattern that Rust rejects:
ⓘ
struct Database { models: Vec<Model> }
struct Model { objects: Vec<Object> }
struct Object { matrix: f64 }
struct Viewer {
db: Database,
model_id: usize,
object_mats: Vec<f64>,
}
impl Viewer {
fn cur_model(&self) -> &Model {
&self.db.models[self.model_id]
}
fn problem(&mut self) {
// ERROR: cannot borrow `self.object_mats` as mutable because
// `self` is also borrowed as immutable (by cur_model)
for obj in &self.cur_model().objects {
self.object_mats.push(obj.matrix);
}
}
}The code is safe because cur_model() only reads db and model_id,
while we’re mutating object_mats. But Rust can’t see through the method boundary.
§The Solution
use fieldwork_borrow::with_;
struct Database { models: Vec<Model> }
struct Model { objects: Vec<Object> }
struct Object { matrix: f64 }
struct Viewer {
db: Database,
model_id: usize,
object_mats: Vec<f64>,
}
impl Viewer {
fn problem(&mut self) {
with_!(self, {
methods {
cur_model(&db, copy model_id) -> &Model {
&db.models[model_id]
}
}
fields {
mut object_mats
}
body {
for obj in &self.cur_model().objects {
object_mats.push(obj.matrix);
}
}
});
}
}§Syntax
with_!(receiver, {
methods {
method_name(field1, mut field2, copy field3, arg: Type) -> ReturnType {
// method body
}
}
fields {
field_a // immutable borrow
mut field_b // mutable borrow
}
body {
// code that uses self.method_name() and self.field_x
}
});§Field Access Modes
In method parameters:
fieldor&field— shared reference (&receiver.field)mut field— mutable reference (&mut receiver.field)copy field— by value, requiresCopytrait
In fields block:
field— shared referencemut field— mutable reference
Macros§
- with_
- Field-level borrow splitting macro.