Help using dyn in wrapper struct around Arc<Mutex<dyn Trait>>


#1

I’m trying to make a small trait for a Send&Sync shared ptr wrapper around a copy type. My initial implementation will use Arc<Mutex<T>> but my goal is to eventually be able to substitute a different wrapper for a heapless implementation that I use in an embedded systems context (stm32).

My initial hurdle is using dyn Trait with my own type. I’ve done it before for other structs that I’ve written but I’m unclear on what exactly the constraints are for using dyn Trait as the type for my generic.

Below I have my initial wrapper, why can’t I push a usize to the vector if I’ve implemented A for usize?

pub mod shared_ptr {
    pub trait SyncSharedPtr<T>: Send + Sync + Clone + Sized {
        //fn new(inner: T) -> Self
        fn locked<F: FnOnce(&mut T) -> R, R: Copy>(&self, func: F) -> R;
    }

    //an alias that i plan to change depending on the platform
    pub type SPtr<T> = MArc<T>;

    pub struct MArc<T: ?Sized> {
        inner: std::sync::Arc<std::sync::Mutex<T>>
    }

    impl<T> MArc<T> 
    where
        T: Send,
    {
        pub fn new(inner: T) -> Self {
            Self { inner: std::sync::Arc::new(std::sync::Mutex::new(inner)) }
        }
    }

    impl<T> SyncSharedPtr<T> for MArc<T>
    where
        T: Send,
    {
        fn locked<F: FnOnce(&mut T) -> R, R: Copy>(&self, func: F) -> R {
            let mut g = self.inner.lock().unwrap();
            func(&mut *g)
        }
    }

    impl<T> Clone for MArc<T> {
        fn clone(&self) -> Self {
            MArc {
                inner: std::sync::Arc::clone(&self.inner),
            }
        }
    }
}

pub trait A {
    fn a(&self);
}

impl A for usize {
    fn a(&self) {}
}

use shared_ptr::{SPtr, SyncSharedPtr, MArc};
use std::sync::Arc;
use std::sync::Mutex;

fn main() {
    //dyn works with Arc
    let mut x: Vec<Arc<Mutex<dyn A>>> = Vec::new();
    let p = Arc::new(Mutex::new(0usize));
    p.lock().unwrap().a();
    x.push(p.clone());
    
    //why doesn't dyn work with my MArc type?
    let y: Vec<MArc<dyn A>> = Vec::new();
    let p = MArc::new(0usize);
    p.locked(|i| i.a()); //does impl A
    y.push(p);

    let y: Vec<SPtr<dyn A>> = Vec::new();
    let p = SPtr::new(0usize);
    p.locked(|i| i.a()); //does ipml A
    y.push(p);
}

(Playground)


#2

This behavior is accomplished via CoerceUnsized for Arc.

For user types, the easiest way is just to use dyn T from the start.


#3

You need to impl CoerceUnsized, which unfortunately requires nightly. But it would look like this


#4

I don’t understand what you mean here. Am I not using dyn T ?


#5

Thanks @vitalyd and @CAD97,
Seems like this has been waiting for stabilization for a while:

Bummer, was hoping to be able to use stable once NLL lands there…


#6

And a follow up…

How do I use the wrapper trait when I pull the items back out of the container?

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=b62d0baf7906360987ca1d2d62135473


#7

You needed a bit more : ?Sized (the default is <T: Sized, and dyn _ : !Sized) and a guarantee that dyn A : Send:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=9cdab6b7619fa4344c66fd45dd196ed2

And the idea is to keep dyn _ from the start;

fn new<T, U>(t: T) -> MArc<U> {
    MArc(Arc::new(t))
}

new::<usize, dyn _>(0)

or something along those lines. Generically handling dynamic dispatch is rough until we can agree on and stabilize a good Unsize.


#8

Great, THANKS @CAD97