Poperton
Poperton

Reputation: 2148

on-the-fly substitution of `Option<Arc<Mutex<Box<dyn T>>>>`

Suppose I have an object video_source: Option<Arc<Mutex<Box<dyn GetVideo>>>> and I pass it to a thread:

std::thread::spawn(||{
    loop {
        if let Some(video_source) = video_source {
            let video_frame = video_source.lock().unwrap().get();
        }
    }
})

where

trait GetVideo {
    fn get() -> Vec<u8>
}

What if I want to change the video source on the fly? Well, I'd do this on another thread:

video_frame.unwrap().lock().unwrap() = Box::new(other_source);

I want to make this idea more generic. I want a type that permits such thing. Here's my sketch:

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

pub type OnTheFlyInner<T> = Box<T + Send + Sync>;
pub type OnTheFly<T> = Arc<Mutex<OnTheFlyInner<T>>>;

//I'd like this to be a method of `OnTheFly`
pub fn on_the_fly_substitute(on_the_fly: &mut Option<OnTheFly>, substitute_by: Option<OnTheFlyInner>) {
    if let Some(substitute_by) = substitute_by {
        if let Some(on_the_fly) = on_the_fly {
            *on_the_fly.lock().unwrap() = substitute_by;
        }
    } else {
        on_the_fly.take();
    }
}

However, I cannot make something generic over T where T is a trait, it should be a type.

Any ideas?


Bounty

This is solved by @user4815162342. But what if I want to make one OnTheFly object point to the same thing as the other one?

Upvotes: 0

Views: 832

Answers (1)

kmdreko
kmdreko

Reputation: 60052

First, you are correct that T cannnot be a trait like GetVideo; traits are not types. However, T can be dyn GetVideo.

Second, your aliases have generic parameters, so they should be reflected as such in the function signature:

pub fn on_the_fly_substitute<T>(on_the_fly: &mut Option<OnTheFly<T>>, substitute_by: Option<OnTheFlyInner<T>>)
                            ^^^                                 ^^^                                      ^^^

Third, your alias looks like an attempt to constrain T to be Send + Sync, but aliases cannot define additional bounds. You would instead put them on the function (with ?Sized since you want to allow trait objects):

pub fn on_the_fly_substitute<T: ?Sized>(on_the_fly: &mut Option<OnTheFly<T>>, substitute_by: Option<OnTheFlyInner<T>>)
where
    T: ?Sized + Send + Sync
{
    ...
}

Note: your function body does not require Send and Sync so these bounds should probably not be included.

Fourth, Option<Arc<Mutex<Box<dyn GetVideo>>>> is not thread safe. You'll need to constrain that the trait object is at least Send:

Option<Arc<Mutex<Box<dyn GetVideo + Send>>>>
                                  ^^^^^^

Fifth, a complete example is lacking, but you appear to be wanting multiple threads to modify the same video_source. This would likely not compile since you would need multiple threads to keep a &mut _ in order to change it.

If you want shared ownership of a value that might not exist, move the option into the Mutex and adjust your function and aliases accordingly:

video_source: Arc<Mutex<Option<Box<dyn GetVideo>>>>

Sixth, your comment "I'd like this to be a method of OnTheFly" is misguided. Aliases are just aliases, you'd need a method on the aliased Option/Arc type. Keep it as a free function, introduce an extension trait for it, or create it as a wrapper type instead of an alias if you want more fine-grained control.

Upvotes: 5

Related Questions