Expand description

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

  • #[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.

Motivation

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),
                .. ..

some_operation(instance.as_mut(), .. .. // Type one parameter at the call site.

Example

use partial_borrow::prelude::*;
use partial_borrow::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 operate_gate(g: &mut partial!(Garden mut gate_open), open: bool) {
    *g.gate_open = open;
    eprintln!("operate_gate, {:?}", 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.gate_open);
        //    ^ error: type `F_gate_open<No, bool, Garden>` cannot be dereferenced
    }
}

let mut garden = Garden::default();
operate_gate(garden.as_mut(), true);
garden.trees += 1;
let (for_gate, rest) = SplitOff::split_off_mut(&mut garden);
let guest = TreeAdmirer { g: rest.as_ref() };
guest.admire();
operate_gate(for_gate, false);
guest.admire();

Output

operate_gate, Garden__Partial { trees: 0, gate_open: true }
I see 1 trees TreeAdmirer { g: Garden__Partial { trees: 1, gate_open: _ } }
operate_gate, Garden__Partial { trees: 1, gate_open: false }
I see 1 trees TreeAdmirer { g: Garden__Partial { trees: 1, gate_open: _ } }

Method example with easy_ext

use partial_borrow::prelude::*;
use easy_ext::ext;

#[ext]
impl partial!(Garden mut gate_open) {
    pub fn operate_gate(&mut self, open: bool) { /*...*/ }
}

let mut garden = Garden::default();
garden.as_mut().operate_gate(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 replace_with 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.

Re-exports

pub use prelude::*;

Modules

Helper imports for the use of macro-generated code.

Field borrowing permissions.

use * for convenient use of this crate in your module.

Macros

Conveniently specify a partially borrowed struct type, by field(s).

Traits

Methods (downgrade) for downgrading (partial) references.

Helper trait (types that can be partially borrowed).

Methods (split_into) for splitting (partial) references.

Methods (split_off) for splitting off (partial) references.

Derive Macros

Derive partial borrowing for a struct.