How to convert Arc to Arc when a function requires Arc as parameter?

How to convert Arc<Mutex<Box<Struct>>> to Arc<Mutex<Box<dyn Trait>>> when a function requires Arc<Mutex<Box<dyn Trait>>> as parameter?
In the following code snippet, the age_plus function requires a type of Arc<Mutex<Box<dyn Animal>>>, but only a type of Arc<Mutex<Box<Dog>>> is available.

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

pub trait Animal {
    fn age_plus(&mut self) -> u32;
}

pub struct Dog {
    age: u32,
    pub name: String,
}

impl Animal for Dog {
    fn age_plus(&mut self) -> u32 {
        self.age += 1;
        self.age
    }
}
impl Dog {
    pub fn print_name(&self) {
        println!("name: {}", self.name);
    }
}
pub fn age_plus(animal: Arc<Mutex<Box<dyn Animal>>>) {
    animal.lock().unwrap().age_plus();
}
fn main() {
    let dog = Arc::new(Mutex::new(Box::new(Dog {
        age: 10,
        name: "dog".to_string(),
    })));
    dog.lock().unwrap().print_name();
    println!("dog age: {}", dog.lock().unwrap().age_plus());
    // how to convert Arc<Mutex<Box<Dog>>> to Arc<Mutex<Box<dyn Animal>>>?
    age_plus(dog.clone());
}

  • 1

    You can’t directly convert these types without stealing/copying the Dog. However, you could instead implement Animal on Arc<Mutex<Box<T>>> where T: Animal. play.rust-lang.org/…

    – 




  • 2

    Function requiring Arc<Mutex<Box<dyn Trait>>> as its input, is a giant code smell, and will be source of nothing than problems. Consider accepting &mut dyn Trait instead and locking the mutex before you call this function.

    – 

  • Thank you for your answer. If cannot modify the input parameters of age_plus, play.rust-lang.org/…

    – 

  • 1

    @AleksanderKrauze “Giant code smell” could be perceived as somewhat dismissive. The function might have valid reasons to accept the Arc, such as needing to store it somewhere. The OP has obviously simplified the use case for the sake of providing a MRE, not to mention that they might not control the definition of age_plus().

    – 

This cannot be done.

The reason is that Box<Dog> occupies the size of pointer bytes in memory, but Box<dyn Animal> occupies double times the size of a pointer in memory, since it also needs to store the vtable. So Arc<Mutex<Box<Dog>>> needs to grow to contain Arc<Mutex<Box<dyn Animal>>>, and that is impossible.

If you cannot change the function in any way, your only chance is cloning the type and creating a new type:

let dyn_animal = Arc::new(Mutex::new(Box::new(Dog::clone(&dog.lock().unwrap())) as _));
age_plus(dyn_animal);

Of course, Dog must be Clone. You might be able to avoid the clone by storing Box<dyn Animal> from the beginning, but it won’t work if you need to call Dog-specific methods.

Another possible alternative, if you don’t need the Arc<Mutex> at the beginning and don’t need to call Dog-specific methods afterwards, is to only construct Box<dyn Dog>. This is easily convertible to Box<dyn Animal>, and it can be wrapped later with Arc<Mutex>:

fn main() {
    let mut dog = Box::new(Dog {
        age: 10,
        name: "dog".to_string(),
    });
    dog.print_name();
    println!("dog age: {}", dog.age_plus());
    let dog = Arc::new(Mutex::new(dog as Box<dyn Animal>));
    age_plus(Arc::clone(&dog));
}

If you can change the function, you have several options:

The best way, if possible, is to take &mut dyn Animal as @AleksanderKrauze said. This way you can provide all sorts of animals:

pub fn age_plus(animal: &mut dyn Animal) {
    animal.age_plus();
}
fn main() {
    let dog = Arc::new(Mutex::new(Box::new(Dog {
        age: 10,
        name: "dog".to_string(),
    })));
    dog.lock().unwrap().print_name();
    println!("dog age: {}", dog.lock().unwrap().age_plus());
    age_plus(&mut **dog.lock().unwrap());
}

If this is not possible, consider taking Arc<Mutex<dyn Animal>>. Arc<Mutex<Dog>> can be converted to that:

pub fn age_plus(animal: Arc<Mutex<dyn Animal>>) {
    animal.lock().unwrap().age_plus();
}
fn main() {
    let dog = Arc::new(Mutex::new(Dog {
        age: 10,
        name: "dog".to_string(),
    }));
    dog.lock().unwrap().print_name();
    println!("dog age: {}", dog.lock().unwrap().age_plus());
    age_plus(Arc::clone(&dog) as _);
}

Leave a Comment