Rust reset Arc<Mutex<T>> to a new T instance?

use std::sync::{Arc, Mutex};

trait B{}

struct A{
}

impl B for A{}


fn main() {
    let a = A{};
    let b: Arc<Mutex<dyn B>> = Arc::new(Mutex::new(a));
    //Can I lock `b` and change the inner `a`?
    
}

I wanted something like this:

    b.lock().unwrap().reset(A{});

I though of doing

let b: Arc<Mutex<Option<dyn B>>> = Arc::new(Mutex::new(a));

error[E0277]: the size for values of type `dyn B` cannot be known at compilation time
   --> src/main.rs:13:12
    |
13  |     let b: Arc<Mutex<Option<dyn B>>> = Arc::new(Mutex::new(Some(a)));
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn B`

but it won't work, it says `B` is not `Sized`.

Why there's no easy way to do this?

I could explain why I want this but I think the question would be too long. But basically I want to be able to call an Arc<Mutex<dyn B>> even if its inner object gets changed.

No. To do that, the dyn B must be wrapped in a box.

fn main() {
    let a = A {};
    let b: Arc<Mutex<Box<dyn B>>> = Arc::new(Mutex::new(Box::new(a)));
}
*b.lock().unwrap() = Box::new(A {});

This won't work for a couple of reasons:

  1. While A has a trait impl for B, in order to make a trait object i.e. dyn B, that value is what's called a Dynamically Sized Type (DST) and as such must be behind some kind of pointer. So b.lock().unwrap().reset(A{}); should become b.lock().unwrap().reset(Box::new(A{}));
  2. Even after this change, where exactly is the .reset() method supposed to be defined? Mutex has no such method, and neither do MutexGuard or the trait B.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=028a4a999b8192645f06f177d366fa17

Indeed, I've come up with this

use std::sync::{Arc, Mutex};

trait B{
    fn do_something(&self);
}

struct A{
    message: String
}

impl A{
}

impl B for A{
    fn do_something(&self){
        println!("{}", self.message);
    }
}


fn main() {
    let a = A{
        message: "message1".to_string()
    };
    let b: Arc<Mutex<Option<Box<dyn B + Send + Sync>>>> = Arc::new(Mutex::new(Some(Box::new(a))));
    let b_clone = b.clone();
    std::thread::spawn(move ||{
        loop {
            let bb = b_clone.lock().unwrap();
            if bb.as_ref().is_some() {
                bb.as_ref().unwrap().do_something();    
            }
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
    });
    std::thread::sleep(std::time::Duration::from_secs(2));
    let aa = A{
        message: "message2".to_string()
    };
    *b.lock().unwrap() = Some(Box::new(aa));
    std::thread::sleep(std::time::Duration::from_secs(1000));
    println!("end");
}

it works. I can substitute the inside Arc on the fly, and the loop will just keep working on the replaced one without noticing, which was the goal. Do you think this is the simplest way of doing this? I think : Arc<Mutex<Option<Box<dyn B + Send + Sync>>>> is a too heavy signature

It doesn't seem like you are using the Option?

Indeed. I forgot to mention that sometimes the object needs to create the object in advance so I added the Option so it can create one that 'points' to nothing. Later a dyn B can be setted.

Box works fine for me but I was wondering, would it be possible to do without dynamic allocation? Maybe

Arc<Mutex<Option<Arc<dyn B + Send + Sync>>>>

but of couse without Arc in the dyn part because Arc has no interior mutability.

No, to change dynamically you must use dynamic allocation. (But an extra Arc layer is also a form of dynamic allocation)

one final thing. Suppose I make this a type

pub type OnProduce = Arc<Mutex<Box<dyn Fn(EncodedPacket) + Send + Sync>>>;

so it's easy to put on structs that require it:

struct MyStruct {
    on_produce: OnProduce
}

is there a way to easily construct it? Currently I need to do:

let on_produce = Arc::new(Mutex::new(Box::new(|e|{})))

this is too heavy and if by some reason I change the signature of OnProduce in the future, I'd have to change all these creations.

Something like this would be great:

let on_produce = OnProduce::new(|e|{})

I tried but it considers OnProduce = Arc I guess:

use std::sync::{Arc, Mutex};

pub type OnProduce = Arc<Mutex<Box<dyn Fn() + Send + Sync>>>;

fn main() {
    let x = OnProduce::new(||{});
}
 --> src/main.rs:6:28
  |
6 |     let x = OnProduce::new(||{});
  |                            ^^^^ expected struct `Mutex`, found closure
  |
  = note: expected struct `Mutex<Box<(dyn Fn() + Send + Sync + 'static)>>`
            found closure `[closure@src/main.rs:6:28: 6:32]`

You could create a helper function, but besides that no.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.