'Is there an idiomatic way to have a generic over T, &T, Box<T>, Arc<T>, and so forth, where T implements some trait R?

Let's say you have a structure that you want to be a generic over time type T, so long as that type implements some trait R. That's pretty trivial:

trait R {
    fn func_a(&self);
    fn func_b(&mut self);
}
struct A;
impl R for A {
    fn func_a(&self) {}
    fn func_b(&mut self) {}
}
struct B<T>(T) where T: R;

fn main() {
    let _ = B(A);
    // let _ = B(&A); // Error: the trait `R` is not implemented for `&A`
}

But this gets more complicated if you want B to also accept references (&A) or smart pointers (Box<A>, Arc<A>, etc...) One approach is to impl R for references specific smart pointers. For example,

impl R for &A { /*...*/ }
impl R for Box<A> { /*...*/ }
// etc...

While this approach seems simple, it doesn't work when R has methods that take a &mut self because of Rust's mutability rules. And, even if all R's methods do take a &self, the set of smart pointers that can be used are limited to those you explicity put in implementations for, and your code is littered with proxy implementations.

The alternative approach is to use the Deref trait. This works for all the smart pointers at once, but no longer works for the simple owned value. Of course, if you want the resulting B to have a 'static lifetime, it's trivial to create a new Box, but this also means an unnecessary memory allocation.

struct B<T>(T) where T: Deref, T::Target: R;
fn main() {
    // let _ = B(A); // Error: the trait bound `A: Deref` is not satisfied
    let _ = B(&A);
    let _ = B(Box::new(A));
}

Finally, we have the Borrow trait. Becuase the standard library includes a blanket impl Borrow<T> for T, as well as the smart pointers, but this now becomes rather awkward to use. Our structure B needs to have a PhantomData<U>, and our uses need type annotations or they don't compile.

struct B<T, U>(T, PhantomData<U>) where T: Borrow<U>, U: R;
fn main() {
    let _ = B::<&A, A>(&A, Default::default());
    let _ = B::<Box<A>, A>(Box::new(A), Default::default());
    let _ = B(A, Default::default());
}

Of these options, using the Deref trait seems to the cleanest. You can even make it work with an owned value by creating a trivial wrapper type like this OwnedCell.

struct B<T>(T) where T: Deref, T::Target: R;
struct OwnedCell<T>(T);
impl<T> Deref for OwnedCell<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
fn main() {
    let _ = B(&A);
    let _ = B(Box::new(A));
    let _ = B(OwnedCell(A));
}

However, the fact that there doesn't seem to be anything like OwnedCell in the standard library makes me think I might be missing something here. What's the right way to do this?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source