Mutate inner value of Arc (e.g. Arc<Vec<Item>>)

Emir Buğra KÖKSALAN tarafından tarihinde yayınlandı

Do you really want to Mutex for getting mutable reference to inner value of Arc? Actually no. You can use Arc::make_mut(&mut arc_clone) method. Let us code something:

use std::sync::Arc;

#[derive(Debug, Clone)]
struct Item(String);
type ItemsArcType = Arc<Vec<Item>>;

fn main() {
    let mut items_arc: ItemsArcType = Arc::new(Vec::new());
    let items_mut_1 = Arc::make_mut(&mut items_arc);
    items_mut_1.push(Item("foo 1".to_string()));
    items_mut_1.push(Item("foo 2".to_string()));
    println!("Items: {:?}", items_arc);

    let items_mut_2 = Arc::make_mut(&mut items_arc);
    items_mut_2.push(Item("foo 1".to_string()));
    println!("Items: {:?}", items_arc);
}

// ⏯️ play

The output will be like that:

Items: [Item("foo 1"), Item("foo 2")]
Items: [Item("foo 1"), Item("foo 2"), Item("foo 1")]

As you see we can get mutable reference to inner value of Arc without Mutex or RwLock. Note that mutable rules still apply. This means you can’t create multiple mutable reference in single block. So that code will throw error:

fn main() {
    let mut items_arc: ItemsArcType = Arc::new(Vec::new());
    let items_mut_1 = Arc::make_mut(&mut items_arc);
    let items_mut_2 = Arc::make_mut(&mut items_arc);
    items_mut_2.push(Item("foo 1".to_string()));
    items_mut_1.push(Item("foo 1".to_string()));
    items_mut_1.push(Item("foo 2".to_string()));
    //drop(items_mut_1);
    println!("Items: {:?}", items_arc);
}

But there is an important thing about this usage. If you clone the original Arc value and make it mutable than strange behaiours may occur. For example:

fn main() {
    let mut items_arc: ItemsArcType = Arc::new(Vec::new());
    let mut items_arc_clone = items_arc.clone();
    let items_mut_1 = Arc::make_mut(&mut items_arc);
    let items_mut_2 = Arc::make_mut(&mut items_arc_clone);
    items_mut_2.push(Item("1".to_string()));
    items_mut_1.push(Item("2".to_string()));
    items_mut_1.push(Item("3".to_string()));
    println!("Items: {:?}", items_arc);
}

// ⏯️ play

What do you think about the output? If you think that it will write all of values (1, 2, 3) than you’re wrong. The result will be like that:

Items: [Item("2"), Item("3")]

As you can see only the original Arc value allows to mutate inside value. So you can’t modify the cloned Arc even the compiler compiles the code. Let’s try this:

use std::sync::Arc;

#[derive(Debug, Clone)]
struct Item(String);
type ItemsArcType = Arc<Vec<Item>>;

fn main() {
    let mut items_arc: ItemsArcType = Arc::new(Vec::new());
    let mut items_arc_clone = items_arc.clone();
    let items_mut_2 = Arc::make_mut(&mut items_arc_clone);
    items_mut_2.push(Item("1".to_string()));
    println!("Items: {:?}", items_arc);

}

// ⏯️ play

The output will be empty Vec as I said:

Items: []

Don’t forget this behaviour when you use Arc::make_mut(&mut items_arc) method. Be sure that you’re using original Arc value with mutability rules.

Important note: I didn’t test this in multithreaded environment but this is working in async functions and single threaded environments. If you want to use this thing in multithreaded environment I suggest that get mutable ref in only one thread, other threads send message to this manager thread (via MPSC) and the manager thread write data to this mutable Vec. Otherwise you can face some data races like I showed before. Of course you can read data in all threaded/async/sync functions.

Happy coding…

Kategoriler: Rust

Emir Buğra KÖKSALAN

Java & PHP Developer

0 yorum

Bir yanıt yazın

Avatar placeholder

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Time limit is exhausted. Please reload the CAPTCHA.

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.