Skip to content

Instantly share code, notes, and snippets.

@Osspial
Last active June 27, 2018 23:51
Show Gist options
  • Save Osspial/878ba508627dae0114168918602a01de to your computer and use it in GitHub Desktop.
Save Osspial/878ba508627dae0114168918602a01de to your computer and use it in GitHub Desktop.
Interlaced Mutable Borrows

Interlaced Mutable Borrows

Syntax

&imb foo

Goals

Add a mechanism for statically-verified shared mutable state. Also enables partial borrows.

Summary

This is done by introducing a third borrow type as a compromise between immutable borrows and mutable borrows: interlaced mutable borrows. Like immutable borrows they can be taken an unlimited number of times, and like mutable borrows they can be used to mutate the underlying data.

At first glance, adding such a mechanism fundamentally undercuts the goals of the borrow checker. However, it is made safe by only allowing one interlaced borrow to mutate the underlying data at a time, much like the already-existing RefCell infrastructure. In its ground state, an interlaced borrow is unable to mutate the underlying data. However, an interlaced borrow can be upgraded to a mutable borrow as long as another upgrade is not active elsewhere in the scope.

// Example ignoring NLL
let mut x = 0;
{
    let x_imb_1 = &imb x;
    let x_imb_2 = &imb x;

    *x_imb_1 = 1; // x_imb_1 gets upgraded to a mutable borrow.
    // x_imb_1's upgrade ends.

    *x_imb_2 = 2; // x_imb_2 gets upgraded to a mutable borrow.
    // x_imb_2's upgrade ends.

    {
        // x_imb_1 gets upgraded to a mutable borrow while x_mut is active.
        let x_mut = &mut *x_imb_1;

        let x_mut_2 = &mut *x_mut_2; // Fails with a compile-time error.
    }

    {
        // Upgrades can happen by directly referencing the underlying data, too!
        let x_mut = &mut x;

        let x_mut_2 = &mut *x_mut_2; // Fails with a compile-time error.
    }
}

Interlaced borrows may also be downgraded to immutable borrows, during which mutable borrows cannot be taken.

Applications

Partial Borrowing

#[derive(Debug)]
struct Point {
    x: f32,
    y: f32
}

impl Point {
    fn get_x(&mut self) -> &imb f32 {
        &imb self.x
    }
    
    fn get_y(&mut self) -> &imb f32 {
        &imb self.y
    }
}

let mut point = Point {
    x: 4.,
    y: 8.
};

let x_imb = point.get_x();
let x_imb_1 = point.get_x();
let y_imb = point.get_y();

*x_imb = 5.; // Upgrade x_imb to a mutable reference
// Upgrade ends

// "Point{ x: 5, y: 8 }"
println!("{:?}", point); // Downgrades all &imb references to immutable references
// Downgrade ends

*y_imb = 6.; // Upgrades y_imb to a mutable reference
// Upgrade ends

// "Point{ x: 5, y: 6 }"
println!("{:?}", point); // Ditto.

Rules:

  • Can be upgraded to a mutable borrow, but only one upgrade may be active at a time
  • Cannot upgrade when already borrowed immutably or mutably
  • Can only be taken from an &imb or &mut reference
  • When does an upgrade occur?
    • When assigning a value to the dereferenced variable
    • When borrowing the value mutably, whether through an explicit &mut reference or a function call that takes &mut self
    • When a parent object is borrowed mutably
    • When passed to a child scope
      • Independence rule: When multiple interlaced borrows of the same type are passed to a child scope (ie through a function call or struct/enum), no interlaced borrow may borrow from the same value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment