Skip to content

Instantly share code, notes, and snippets.

@DarinM223
Last active September 29, 2024 13:38
Show Gist options
  • Save DarinM223/fdf65dcc853d5c445d32e862e1c40016 to your computer and use it in GitHub Desktop.
Save DarinM223/fdf65dcc853d5c445d32e862e1c40016 to your computer and use it in GitHub Desktop.
Cyclical mutable references in Rust using GhostCell
/// An example of cyclical mutable references in Rust using GhostCell.
/// Struct A contains mutable references to B and C and B has a
/// mutable reference to C and C has a mutable reference to B.
///
/// Unfortunately, doing it this way means forgoing dot syntax for B and C, since
/// only Rc, Arc, and Pin can be used for self, not &'arena GhostCell<'id, Self>.
use bumpalo::Bump;
use ghost_cell::{GhostCell, GhostToken};
struct A<'arena, 'id> {
b: &'arena GhostCell<'id, B<'arena, 'id>>,
c: &'arena GhostCell<'id, C<'arena, 'id>>,
token: GhostToken<'id>,
}
impl<'arena, 'id> A<'arena, 'id> {
fn new(bump: &'arena Bump, mut token: GhostToken<'id>) -> A<'arena, 'id> {
let b = GhostCell::from_mut(bump.alloc(B { i: 0, c: None }));
let c = GhostCell::from_mut(bump.alloc(C { i: 1, b }));
b.borrow_mut(&mut token).c = Some(c);
A { b, c, token }
}
fn handle_b(&mut self) {
B::handle(self.b, &mut self.token);
}
fn handle_c(&mut self) {
C::handle(self.c, &mut self.token);
}
}
struct B<'arena, 'id> {
i: i32,
c: Option<&'arena GhostCell<'id, C<'arena, 'id>>>,
}
impl<'arena, 'id> B<'arena, 'id> {
fn handle(this: &'arena GhostCell<'id, Self>, token: &mut GhostToken<'id>) {
println!("Handling B with i: {}", this.borrow(token).i);
if this.borrow(token).i > 10 {
return;
}
this.borrow_mut(token).i += 1;
let c = this.borrow(token).c.unwrap();
c.borrow_mut(token).i += 1;
C::handle(c, token);
}
}
struct C<'arena, 'id> {
i: i32,
b: &'arena GhostCell<'id, B<'arena, 'id>>,
}
impl<'arena, 'id> C<'arena, 'id> {
fn handle(this: &'arena GhostCell<'id, Self>, token: &mut GhostToken<'id>) {
println!("Handling C with i: {}", this.borrow(token).i);
let b = this.borrow(token).b;
b.borrow_mut(token).i += 1;
B::handle(b, token);
}
}
fn main() {
let bump = Bump::new();
GhostToken::new(|token| {
let mut a = A::new(&bump, token);
a.handle_b();
a.handle_c();
});
}
/// An example of cyclical mutable references in Rust using GhostCell.
/// Struct A contains mutable references to B and C and B has a
/// mutable reference to C and C has a mutable reference to B.
///
/// Unfortunately, doing it this way means forgoing dot syntax for B and C, since
/// only Rc, Arc, and Pin can be used for self, not &'a GhostCell<'id, Self>.
use ghost_cell::{GhostCell, GhostToken};
struct A<'a, 'id> {
b: &'a GhostCell<'id, B<'a, 'id>>,
c: &'a GhostCell<'id, C<'a, 'id>>,
token: GhostToken<'id>,
}
impl<'a, 'id> A<'a, 'id> {
fn new(
b: &'a GhostCell<'id, B<'a, 'id>>,
c: &'a GhostCell<'id, C<'a, 'id>>,
token: GhostToken<'id>,
) -> A<'a, 'id> {
A { b, c, token }
}
fn handle_b(&mut self) {
B::handle(self.b, &mut self.token);
}
fn handle_c(&mut self) {
C::handle(self.c, &mut self.token);
}
}
struct B<'a, 'id> {
i: i32,
c: Option<&'a GhostCell<'id, C<'a, 'id>>>,
}
impl<'a, 'id> B<'a, 'id> {
fn handle(this: &'a GhostCell<'id, Self>, token: &mut GhostToken<'id>) {
println!("Handling B with i: {}", this.borrow(token).i);
if this.borrow(token).i > 10 {
return;
}
this.borrow_mut(token).i += 1;
let c = this.borrow(token).c.unwrap();
c.borrow_mut(token).i += 1;
C::handle(c, token);
}
}
struct C<'a, 'id> {
i: i32,
b: &'a GhostCell<'id, B<'a, 'id>>,
}
impl<'a, 'id> C<'a, 'id> {
fn handle(this: &'a GhostCell<'id, Self>, token: &mut GhostToken<'id>) {
println!("Handling C with i: {}", this.borrow(token).i);
let b = this.borrow(token).b;
b.borrow_mut(token).i += 1;
B::handle(b, token);
}
}
fn main() {
GhostToken::new(|mut token| {
let mut b_owned = B { i: 0, c: None };
let b = GhostCell::from_mut(&mut b_owned);
let mut c_owned = C { i: 1, b };
let c = GhostCell::from_mut(&mut c_owned);
b.borrow_mut(&mut token).c = Some(c);
let mut a = A::new(b, c, token);
a.handle_b();
a.handle_c();
});
}
/// An example of shared mutable references in Rust using GhostCell.
/// Struct A contains structs B and C and all of them have a mutable reference
/// to the same state, Shared.
use bumpalo::Bump;
use ghost_cell::{GhostCell, GhostToken};
struct A<'arena, 'id> {
shared: &'arena GhostCell<'id, Shared>,
b: B<'arena, 'id>,
c: C<'arena, 'id>,
bump: &'arena Bump,
token: GhostToken<'id>,
}
impl<'arena, 'id> A<'arena, 'id> {
fn new(bump: &'arena Bump, token: GhostToken<'id>) -> A<'arena, 'id> {
let shared = GhostCell::from_mut(bump.alloc(Shared { counter: 0 }));
let b = B { shared };
let c = C { shared };
A {
shared,
b,
c,
bump,
token,
}
}
fn change_shared(&mut self) {
self.shared = GhostCell::from_mut(self.bump.alloc(Shared { counter: 1 }));
self.b.shared = self.shared;
self.c.shared = self.shared;
}
fn incr_twice(&mut self) -> (i32, i32) {
(self.b.incr(&mut self.token), self.c.incr(&mut self.token))
}
fn incr_fourth(&mut self) -> ((i32, i32), (i32, i32)) {
self.shared.borrow_mut(&mut self.token).incr();
(self.incr_twice(), self.incr_twice())
}
}
struct B<'arena, 'id> {
shared: &'arena GhostCell<'id, Shared>,
}
impl<'arena, 'id> B<'arena, 'id> {
fn incr(&self, token: &mut GhostToken<'id>) -> i32 {
self.shared.borrow_mut(token).incr()
}
}
struct C<'arena, 'id> {
shared: &'arena GhostCell<'id, Shared>,
}
impl<'arena, 'id> C<'arena, 'id> {
fn incr(&self, token: &mut GhostToken<'id>) -> i32 {
self.shared.borrow_mut(token).incr()
}
}
struct Shared {
counter: i32,
}
impl Shared {
fn incr(&mut self) -> i32 {
let result = self.counter;
self.counter += 1;
result
}
}
fn main() {
let bump = Bump::new();
GhostToken::new(|token| {
let mut a = A::new(&bump, token);
println!("{:?}", a.incr_fourth());
println!("{:?}", a.incr_twice());
a.change_shared();
println!("{:?}", a.incr_fourth());
println!("{:?}", a.incr_twice());
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment