Why are we able to change the content of Box?


#1

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?


#2

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)


#3

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.


#4

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.


How immutable is immutable?
#5

Sync lets values be shared behind immutable references. See this example with Arc: https://play.rust-lang.org/?gist=cb332bcd2eb8a23c3b44&version=stable. 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.


#6

If you want to understand what Sync really means, check out http://manishearth.github.io/blog/2015/05/30/how-rust-achieves-thread-safety/


#7

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.