Expand description
§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 in the same thread. This ensures that we
can only mutably borrow one KeyCell per thread.
Once we have a mutable borrow of a KeyCell we can relinquish this borrow
temporarily using Key::split_rw to get
&mut Key, and borrow other KeyCells. This is what allows us to implement
recursive calls with mutable access.
§Comparison to RefCell
Unlike RefCell, borrows on KeyCells will never fail. They also don’t
need to perform any runtime checks. All borrow checking is performed at
compile time.
- Borrowing is zero-cost.
- Borrowing will never fail or panic.
- Only a single
KeyCellcan be mutably borrowed per thread.
§Alternatives
The core idea in this crate relies on acquiring mutable borrows through a unique object (the Key). The same idea is implemented in a few other crates.
§Examples
This crate uses ro and
rw instead of borrow and borrow_mut to access data.
use mutcy::{Key, KeyCell, Rw};
let mut key = Key::acquire();
let kc1 = KeyCell::new(0i32, ());
let kc2 = KeyCell::new(String::new(), ());
*kc2.rw(&mut key) += "Hello";
*kc1.rw(&mut key) += 1;
*kc2.rw(&mut key) += "World";
let item1 = kc1.ro(&key);
let item2 = kc1.ro(&key);
println!("{} - {}", *item1, *item2);With this library it’s possible to define methods that take Rw and transfer mutability to other KeyCells 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: Rw<Self> and relinquish its own borrows to access some other
KeyCell.
#![feature(arbitrary_self_types)]
use mutcy::{Key, KeyCell, Meta, Rw};
use std::rc::Rc;
struct MyStruct {
value: i32,
}
impl MyStruct {
fn my_function(self: Rw<Self>) {
self.value += 1;
// This relinquishes any borrows to `self`.
let (this, key) = Key::split_rw(self);
// We can now access any other KeyCell using `key`.
let mut string = this.meta().other.rw(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.rw(&mut key).my_function();
println!("{}", *other.ro(&key));
println!("{}", my_struct.ro(&key).value);§Metadata
KeyCell::new takes two arguments, the primary data to wrap, and metadata. Metadata can always be accessed immutably as it does not require a Key.
This allows us to reference and access other KeyCells without having to
clone an Rc<KeyCell<_>> out of the initial item.
For more details see Meta.
Structs§
- Callback
- Callback defined on a KeyCell.
- Connection
- References a connection created via Signal::connect.
- Key
- A per-thread unique key used to access data inside KeyCells.
- KeyCell
- A
RefCell-like wrapper that can only be accessed via Key. - RoGuard
- Wraps an immutably borrowed reference to a value in a
KeyCell. - RwGuard
- Wraps a mutably borrowed reference to a value in a
KeyCell. - Signal
- Holds weak references to KeyCells on which callbacks can be run.
Traits§
- Into
Weak - Converts Rc or Weak into a
Weak. - Meta
- Defines metadata to be stored alongside the primary item in a KeyCell.