Crate partial_ref

source ·
Expand description

Type checked partial references.

This crate provides type checked partial references for rust. Type checked partial references are one solution to solve interprocedural borrowing conflicts.

Tutorial

With this crate it is possible to declare typed parts (using the part macro) and to assign struct fields to these parts (by deriving PartialRefTarget).

use partial_ref::{part, PartialRefTarget};

part!(pub Neighbors: Vec<Vec<usize>>);
part!(pub Colors: Vec<usize>);
part!(pub Weights: Vec<f32>);

#[derive(PartialRefTarget, Default)]
pub struct Graph {
    #[part = "Neighbors"]
    pub neighbors: Vec<Vec<usize>>,
    #[part = "Colors"]
    pub colors: Vec<usize>,
    #[part = "Weights"]
    pub weights: Vec<f32>,
}

It is then possible to convert a reference to a value using the IntoPartialRef and IntoPartialRefMut traits, which are derived alongside PartialRefTarget. Note that a mutable partial reference must itself be mutable for mutable access.

use partial_ref::IntoPartialRefMut;

let mut g = Graph::default();

let mut g_ref = g.into_partial_ref_mut();

You can access parts using the part and part_mut methods.

use partial_ref::PartialRef;

g_ref.part_mut(Colors).extend(&[0, 1, 0]);
g_ref.part_mut(Weights).extend(&[0.25, 0.5, 0.75]);

g_ref.part_mut(Neighbors).push(vec![1, 2]);
g_ref.part_mut(Neighbors).push(vec![0, 2]);
g_ref.part_mut(Neighbors).push(vec![0, 1]);

assert_eq!(g_ref.part(Colors).len(), g_ref.part(Neighbors).len());
assert_eq!(g_ref.part(Colors).len(), g_ref.part(Weights).len());

We can now write a function that takes parts of a reference. The type of such a partial reference can be written using the partial macro, which expands to a combination of Mut, Const and Ref. Again the parameter g here must be mutable to allow mutable access to the referenced value. To call such a function we use the borrow method, which will re-borrow just the required parts.

use partial_ref::partial;

pub fn add_color_to_weight(
    mut g: partial!(Graph, mut Weights, Colors),
    index: usize,
) {
    g.part_mut(Weights)[index] += g.part(Colors)[index] as f32;
}

add_color_to_weight(g_ref.borrow(), 1);

assert_eq!(g_ref.part(Weights)[1], 1.5);

So far everything could have been written using plain built-in references. This changes as soon as we want to iterate over the neighbors while invoking our function. Usually we couldn’t pass a mutable reference to the graph while holding the iterator over the neighbors.

This can be easily done using partial references which support splitting. Splitting means turning a single reference into two references where each has a subset of parts, so that each mutably referenced part belongs only to a single reference. This is done by the split_borrow and split_part methods.

let (neighbors, mut g_ref) = g_ref.split_part_mut(Neighbors);
let (colors, mut g_ref) = g_ref.split_part(Colors);

for (edges, &color) in neighbors.iter_mut().zip(colors.iter()) {
    edges.retain(|&neighbor| colors[neighbor] != color);

    for &neighbor in edges.iter() {
        add_color_to_weight(g_ref.borrow(), neighbor);
    }
}

This covers the basic functionality of this library. Beyond that this library also supports:

  • Partial references to nested structs using Nested and nested_part.
  • Generic functions with bounds on available parts using HasPart.

Notes

Some documented items are marked with (internal). Typical code using this library doesn’t explicitly refer to them. Nevertheless they often appear in error messages and are thus part of this documentation. These items also have to be public for the inference driven meta programming to work. Code that is generic over parts of partial references might also need them.

Many traits in this crate are marked as unsafe without documenting any requirements for implementations. This does not mean they are safe to implement, but rather means that they are not intended to be implemented outside of this library. Feel free to file an issue if you have a good reason to implement them so the requirements can be documented.

Macros

Expands A | B | ... | Z to Nested<...Nested<A, B>, ..., Z>
Declares a [Part].
Concise syntax for partial reference types.
Helper macro for splitting a partial reference.

Structs

Type of an abstract part.
A constant (immutable) part of a partial reference.
Type of a part that corresponds to a struct field.
(internal) Select the first part.
(internal) Skip the first part.
(internal) Split the first part.
A mutable part of a partial reference.
A nested part.
An empty partial reference borrowing no parts.
(internal) Construct a subset index from a part index and another subset index.
(internal) Index for the empty subset.

Traits

(internal) Check whether a part is nested inside another part.
Implemented when a reference target has a part.
(internal) Asserts that the parts of the partial reference Reference are a subset of the parts of the partial reference having this trait.
Construction of partial references.
Construction of partial references from mutable references.
Marker types for a part of a type.
Type of a part, determines what can be done with a part.
A partial reference.
A type that can be the target of partial references.
(internal) Extracts the constant part PluckedPart at position Index from the partial reference having this trait, leaving Self::Remainder.
(internal) Extracts the mutable part PluckedPart at position Index from the partial reference having this trait, leaving Self::Remainder.
(internal) Split a part into nested parts.

Derive Macros

Derives instances of PartialRefTarget and associated traits.