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.

Primary reference documentation and entrypoints

Motivation

Before ```

use std::ops::*;

type GameState=(); type IPieces=(); type IOccults=();

#[derive(Default)]

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.

_: RangeTo) { }

let _ =

              .. ..

;

let mut instance = Instance::default();

some_operation(&mut instance.gs, // Much typing to pass each field. &instance.ipieces, &instance.ioccults, .. ..

);

After

use std::ops::*;

use partial_borrow::prelude::*;

type GameState=(); type IPieces=(); type IOccults=();

#[derive(PartialBorrow,Default)]

struct Instance { gs: GameState, ipieces: IPieces, ioccults: IOccults };

fn some_operation(// One pointer. Need only list fields to be mut, here. g: &mut partial!(Instance mut gs),

_: RangeTo) { }

let _ =

            .. ..

;

let mut instance = Instance::default();

someoperation(instance.asmut(), .. .. // Type one parameter at the call site.

);

```

Example

``` use partialborrow::prelude::*; use partialborrow::SplitOff;

[derive(Debug,PartialBorrow,Default)]

[partial_borrow(Debug)]

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); }

[derive(Debug)]

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: _ } } ```

Method example with easy_ext

```

#[derive(Default,PartialBorrow)]

pub struct Garden { gate_open: bool }

use partialborrow::prelude::*; use easyext::ext;

[ext]

impl partial!(Garden mut gateopen) { pub fn operategate(&mut self, open: bool) { /.../ } }

let mut garden = Garden::default(); garden.asmut().operategate(true); ```

MIRI

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'

Safety

The provided API is supposed to be safe and sound.

Compatibility - language assumption about owned values from references

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.

Assurance

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.