1

I have two structs that refer to each other. Once initialized, they are never mutated for the rest of the application's lifetime.

It's possible to wrap them in a Mutex or RwLock or something, but it'd be nice not to deal with those throughout the codebase, just to get things initialized.

Here's the example code (does not compile):

use std::sync::Arc;
struct First {
    second: Option<Second>,
}

struct Second {
    first: Option<Arc<First>>,
}

fn main() {
    let first = Arc::new(First { second: None });
    let mut second = Second { first: None };
    second.first = Some(first.clone());
    first.second = Some(second);
}

The problem:

error[E0594]: cannot assign to data in an `Arc`
  --> src/main.rs:14:5
   |
14 |     first.second = Some(second);
   |     ^^^^^^^^^^^^ cannot assign
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<First>`

It's clear what's happening here; it's not possible to assign to first, because it's inside an Arc, which does not allow interior mutability.

Changing the order of operations doesn't get around the problem.

Using Arc::get_mut() won't work, because the Arc is cloned for storage in second.first.

So, is it possible to create objects with this pattern without needing runtime locking?

squidpickles
  • 1,737
  • 19
  • 27

1 Answers1

2

You have two options, depending on if you need Sync or not.

With Sync you need some synchronization structure, a RwLock should do:

use std::borrow::BorrowMut;
use std::sync::RwLock;
use std::sync::Arc;

struct First {
    second: Option<Second>,
}

struct Second {
    first: Option<Arc<RwLock<First>>>,
}

fn main() {
    let first = Arc::new(RwLock::new(First { second: None }));
    let mut second = Second { first: None };
    second.first = Some(first.clone());
    let mut inner = first.write().unwrap();
    inner.second = Some(second);
}

Playground

If you do not need Sync, use Rc instead of Arc and RefCell:

use std::rc::Rc;
use std::cell::RefCell;

struct First {
    second: Option<Second>,
}

struct Second {
    first: Option<Rc<RefCell<First>>>,
}

fn main() {
    let first = Rc::new(RefCell::new(First { second: None }));
    let mut second = Second { first: None };
    second.first = Some(first.clone());
    first.borrow_mut().second = Some(second);
}

Playground

Netwave
  • 40,134
  • 6
  • 50
  • 93