#cyclic #mutable #borrow #zero-cost #mutable-borrow

mutcy

Zero-cost safe mutable cyclic borrows using borrow relinquishing

11 unstable releases (3 breaking)

Uses new Rust 2024

new 0.4.1 Apr 17, 2025
0.4.0 Apr 16, 2025
0.3.2 Apr 5, 2025
0.2.0 Apr 4, 2025
0.1.4 Apr 2, 2025

#520 in Rust patterns

Download history 476/week @ 2025-03-29 365/week @ 2025-04-05 235/week @ 2025-04-12

1,076 downloads per month

MIT license

15KB
137 lines

Mutcy

Zero-cost mutable cyclic borrows using borrow relinquishing.

crates.io Documentation MIT licensed


lib.rs:

Zero-Cost Mutable Cyclic Borrows

A RefCell-like dynamic borrowing system that permits recursive borrowing with zero runtime overhead.

This crate implements [KeyCell] of which only a single instance can be mutably borrowed at a time using a [Key]. Simultaneous immutable borrows are permitted.

[Key] is a ZST, and only a single instance can exist per thread. The only dynamic check this library performs is upon calling Key::acquire to ensure that there exists no other instance.

Comparison to RefCell

Unlike RefCell, borrows on [KeyCell]s will never fail. They also don't need to perform any runtime checks. All borrow checking is performed at compile time.

  1. Borrowing is zero-cost.
  2. Borrowing will never fail or panic.
  3. Only a single [KeyCell] can be mutably borrowed per thread.

Examples

This crate uses borrow and borrow_mut to access data.

use mutcy::{Key, KeyCell, KeyMut};

let mut key = Key::acquire();

let kc1 = KeyCell::new(0i32, ());
let kc2 = KeyCell::new(String::new(), ());

*kc2.borrow_mut(&mut key) += "Hello";
*kc1.borrow_mut(&mut key) += 1;
*kc2.borrow_mut(&mut key) += "World";

let item1 = kc1.borrow(&key);
let item2 = kc1.borrow(&key);

println!("{} - {}", *item1, *item2);

With this library it's possible to define methods that take [KeyMut] and transfer mutability to other [KeyCell]s when needed. The compile-time borrow checker ensures that no mutable aliasing occurs.

In the following example we show how a struct can accept a self: KeyMut<Self> and relinquish its own borrows to access some other KeyCell.

#![feature(arbitrary_self_types)]
use mutcy::{Key, KeyCell, KeyMut, Meta};
use std::rc::Rc;

struct MyStruct {
    value: i32,
}

impl MyStruct {
    fn my_function(self: KeyMut<Self>) {
        self.value += 1;

        // This relinquishes any borrows to `self`.
        let (this, key) = Key::split(self);

        // We can now access any other KeyCell using `key`.
        let mut string = this.meta().other.borrow_mut(key);
        *string += "Hello world";

        self.value += 1;
    }
}

struct MyStructMeta {
    other: Rc<KeyCell<String>>,
}

impl Meta for MyStruct {
    type Data = MyStructMeta;
}

let mut key = Key::acquire();

let other = Rc::new(KeyCell::new(String::new(), ()));

let my_struct = KeyCell::new(
    MyStruct { value: 0 },
    MyStructMeta {
        other: other.clone(),
    },
);

my_struct.borrow_mut(&mut key).my_function();

println!("{}", *other.borrow(&key));
println!("{}", my_struct.borrow(&key).value);

For more information on metadata see [Meta].

No runtime deps