Generic for `&dyn A` or `Box<dyn A>` (or `Arc<dyn A>`)

Essentially, how to make the below compile: I am looking for a way to accept a reference-like trait object in the struct, allowing the user to decide the reference type that it is declared as.

trait Array {}

struct AA {}

impl Array for AA {}

struct Columns<A: AsRef<dyn Array>> {
    a: A
}

fn main() {
    let a = AA {};
    
    let c = Columns {a: &a as &dyn Array};
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `dyn Array: AsRef<(dyn Array + 'static)>` is not satisfied
  --> src/main.rs:14:13
   |
14 |     let c = Columns {a: &a as &dyn Array};
   |             ^^^^^^^ the trait `AsRef<(dyn Array + 'static)>` is not implemented for `dyn Array`
   |
   = note: required because of the requirements on the impl of `AsRef<(dyn Array + 'static)>` for `&dyn Array`
note: required by `Columns`
  --> src/main.rs:7:1
   |
7  | struct Columns<A: AsRef<dyn Array>> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

The solution I found so far was to use std::borrow::Borrow instead of AsRef

These notes from the AsRef documentation seems relevant:

AsRef has the same signature as Borrow , but Borrow is different in few aspects:

  • Unlike AsRef , Borrow has a blanket impl for any T , and can be used to accept either a reference or a value.
  • Borrow also requires that Hash , Eq and Ord for borrowed value are equivalent to those of the owned value. For this reason, if you want to borrow only a single field of a struct you can implement AsRef , but not Borrow .

Your OP didn't work because AsRef has no blanket implementation. Borrow does, but the additional requirements around Hash, etc, mean that you should always get a reference that covers the entire owned value in some sense, where as with AsRef you might get a reference to a single field, say (or references to different fields, since you can have multiple AsRef implementations).

I'm not sure you really wanted those extra constraints, in which case you can use AsRef by adding:

    impl<'arr> AsRef<(dyn Array + 'arr)> for dyn Array {
        fn as_ref(&self) -> &(dyn Array + 'arr) {
            self
        }
    }

Example. But if all you care about are the types in your subject line, Borrow should be sufficient.


Here's another approach which may or may not apply to your use case.

    impl<'a, T: Array + ?Sized> Array for &'a T {}
    impl<T: Array + ?Sized> Array for Box<T> {}
    impl<T: Array + ?Sized> Array for std::sync::Arc<T> {}

    pub struct Columns<A: Array> {
        pub a: A,
    }
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.