Partially borrow a struct. Multiple simultaenous mutable partial borrows are possible, so long as each field is only mutably accessible via (at most) one of them.
#[derive(PartialBorrow)]
Derive partial borrowing for a struct.partial!()
]
Conveniently specify a partially borrowed struct type,
by its field(s).use partial_borrow::
[prelude
]::*
for convenient use of this crate in your module.Before ```
struct Instance { gs: GameState, ipieces: IPieces, ioccults: IOccults, /.. ../ };
fn some_operation(gs: &mut GameState, // Several pointers, ipieces: &IPieces, // all to fields of struct Instance. ioccults: &IOccults, // We only want to pass partial mut.
.. ..
some_operation(&mut instance.gs, // Much typing to pass each field. &instance.ipieces, &instance.ioccults, .. ..
After
fn some_operation(// One pointer. Need only list fields to be mut, here. g: &mut partial!(Instance mut gs),
.. ..
someoperation(instance.asmut(), .. .. // Type one parameter at the call site.
```
``` use partialborrow::prelude::*; use partialborrow::SplitOff;
pub struct Garden { trees: usize, gate_open: bool, }
// This can't be an inherent method but it could be an extension
// trait method using a crate like easy_ext
.
pub fn operategate(g: &mut partial!(Garden mut gateopen), open: bool) {
*g.gateopen = open;
eprintln!("operategate, {:?}", g);
}
struct TreeAdmirer<'g> {
g: &'g partial!(Garden const trees, !*),
}
impl TreeAdmirer<'> {
fn admire(&self) {
eprintln!("I see {} trees {:?}", *self.g.trees, self);
// XX eprintln!("gate open? {}", *self.g.gateopen);
// ^ error: type F_gate_open<No, bool, Garden>
cannot be dereferenced
}
}
let mut garden = Garden::default();
operategate(garden.asmut(), true);
garden.trees += 1;
let (forgate, rest) = SplitOff::splitoffmut(&mut garden);
let guest = TreeAdmirer { g: rest.asref() };
guest.admire();
operategate(forgate, false);
guest.admire();
Output
text
operategate, GardenPartial { trees: 0, gateopen: true }
I see 1 trees TreeAdmirer { g: GardenPartial { trees: 1, gateopen: _ } }
operategate, GardenPartial { trees: 1, gateopen: false }
I see 1 trees TreeAdmirer { g: GardenPartial { trees: 1, gateopen: _ } }
```
```
use partialborrow::prelude::*; use easyext::ext;
impl partial!(Garden mut gateopen) { pub fn operategate(&mut self, open: bool) { /.../ } }
let mut garden = Garden::default(); garden.asmut().operategate(true); ```
The library currently relies on integer/pointer conversions,
"exposing" its pointers, so as to be able to recreate references
with appropriate provenance.
The integer conversions are necessary
in lie of
a feature corresponding to
CHERI C/C++'s
__attribute__((cheri_no_subobject_bounds))
(p16 in the CHERI PDF),
or some other way to make a ZST reference without
narrowing the provenance.
As of Nightly 2022-06-24,
the Rust Strict Provenance experiment
does not provide such a feature at the Rust API,
Therefore, running MIRI requires setting
MIRIFLAGS+=' -Zmiri-permissive-provenance'
The provided API is supposed to be safe and sound.
partial-borrow
relies for soundness on the fact that
Rust does not permit the programmer to obtain an owned value T
,
if they are given only reference &mut T
.
(Assuming some type T
which doesn't specifically allow this.)
However, there are some Rust libraries which use unsafe
to extend the Rust language to provide this facility.
For example,
the combination of partial-borrow
with owning-ref
is unsound.
The implementation involves an awful lot of proc-macro-generated
unsafe
. There are tests with miri and a
correctness argument
in the
form of extensive hand-written annotations to an autogenerated output.
There has not been any independent review, and no attempt at
validation using formal methods.
The macro-generated code refers extensively to items from this crate,
under its canonical name partial_borrow
. Using that name for
something else is in theory unsound, although it is highly unlikely
that the proc-macro output would compile if that name somehow referred
to an unrelated crate, so this does not seem to be a practical
concern.