Why are we able to change the content of Box?

Why can we create code like that:

let x = Box::new(42);
let mut y = x;
*y = 100;

Box is implementing Sync. Isn't its content immutable?

No, as you've said

let mut y = x;

This moves the box from an immutable to an mutable binding. As such, you can mutate what's in the box.

(Sync has nothing to do with any of this)

Then there is something that I don't understand. If Box is implementing Sync, how can its value be changed? This can create a race condition. I don't get it. Please explain.

I did some testing. Now I understand. If I try to access a variable of type Box from another thread, the thread will own the value and it won't be accessible from the previous reference.

1 Like

Sync lets values be shared behind immutable references. See this example with Arc: Rust Playground. Doing an explicit deref of the Arc only gets you an immutable pointer to its interior.

So if you put a Box in an Arc you can't get a mutable pointer to the Box - it is 'frozen'. This is what allows immutable data to be shared trivially.

The magic then that allows Sync things to be mutated is 'interior mutability'. Interior mutability provides a way to mutate things behind 'immutable' (i.e. shared) references.

In a concurrent setting, e.g. Mutex can be used to provide exclusive mutable access to a value. So to mutate a shared box you put a Box in a Mutex in an Arc.

1 Like

If you want to understand what Sync really means, check out How Rust Achieves Thread Safety - In Pursuit of Laziness

With Rc and Arc, deferencing is automatic. So we can write:

use std::sync::Arc;
use std::ops::Deref;

fn main() {
    let foo = Arc::new(0);
    let bar: &i8 = &foo.deref();
    println!("{}", foo);
}

And it will print 0.