Home > database >  Converting a generic function argument to an `Arc` with a generic trait
Converting a generic function argument to an `Arc` with a generic trait

Time:12-23

Is it possible to have a function with a generic argument that converts that argument to an Arc with a generic trait? Something like this:

struct Foo<TypeOrTrait> {
    arc: Option<Arc<TypeOrTrait>>,
}

impl<TypeOrTrait> Foo<TypeOrTrait> {
    fn set<T>(&mut self, t: T) -> Arc<T> {
        let arc = Arc::new(t);
        self.arc = Some(arc.clone()); // Error
        arc
    }
}

The code complains that it can't convert Arc<T> to Arc<TypeOrTrait>.

In my code I don't have just the Arc that wraps TypeOrTrait, but a complex Arc<RwLock<Inner<TypeOrTrait>>> so I can't work around this by passing Arc<TypeOrTrait> to the set function.

The use case I have is that I want to have a collection that only implements a trait, but also want to have a way to access/modify the values using their original types. Something like this:

let foo: Foo<fmt::Display> = Foo { arc: None };

let value = foo.set(1u8);
assert_eq!(*value, 1u8);
assert_eq!(foo.bar.unwrap().to_string(), "1");

let value = foo.set(2u16);
assert_eq!(*value, 2u16);
assert_eq!(foo.bar.unwrap().to_string(), "2");

I was told that this would require higher kinded types, is that correct? If so, could it be emulated with something like what they do here (my Rust fu isn't on a par with that)?

CodePudding user response:

If you have only few traits and types, you can use a custom trait:

pub trait ConvertArc<To: ?Sized> {
    fn convert(self: Arc<Self>) -> Arc<To>;
}

impl<T: ?Sized> ConvertArc<T> for T {
    fn convert(self: Arc<Self>) -> Arc<Self> { self }
}
impl ConvertArc<dyn Trait> for Struct {
    fn convert(self: Arc<Self>) -> Arc<dyn Trait> { self }
}

struct S<TypeOrTrait: ?Sized> {
    arc: Option<Arc<TypeOrTrait>>,
}

impl<TypeOrTrait: ?Sized> S<TypeOrTrait> {
    fn set<T: ConvertArc<TypeOrTrait>>(&mut self, t: T) -> Arc<T> {
        let arc = Arc::new(t);
        self.arc = Some(arc.clone().convert());
        arc
    }
}

If you have many traits or types... well... I think you don't have a solution. You can almost use std::marker::Unsize on nightly, but not, because you want to be able to use the same type for both too.

  • Related