Trait that is trivially implemented isn't?

I've got a trait,

pub trait AsyncConnection: AsyncBufReadPlus + AsyncWrite + Send + Unpin {}

I'm passing a &'a mut dyn AsyncConnection to a function fn new_fixed_length(read: R, len: u64) -> Body<R> ; R was AsyncRead. This works fine (trait AsyncBufReadPlus: AsyncRead). But then I needed some methods that were part of AsyncBufReadPlus, and so I changed the AsyncRead to AsyncBufReadPlus; what I thought would be a trivial change. Now, however, I get:

the trait AsyncBufReadPlus is not implemented for &'a mut (dyn AsyncConnection + 'a)

But it seems pretty trivially implemented? Doesn't the trait AsyncConnection line imply that it must implement that trait?

Link to an example in the playground; it is set up with the failing example. The commented out lines can be exchanged with their corresponding lines to switch it back to AsyncRead, where it compiles.

I think that T: AsyncConnection implies T: AsyncBufReadPlus, but T: AsyncConnection does not imply &'_ mut T: AsyncBufReadPlus.

To see the underlying issue, try replacing dyn AsyncConnection with dyn AsyncBufReadPlus:


fn get_body_reader<'a>(
    connection: &'a mut dyn AsyncBufReadPlus,
) -> Result<Body<&'a mut dyn AsyncBufReadPlus>, ()> {

    Ok(Body::new_fixed_length(connection, 9001))
}

You'll get an error

error[E0277]: the trait bound `&'a mut (dyn AsyncBufReadPlus + 'a): AsyncBufReadPlus` is not satisfied
  --> src/lib.rs:31:1
   |
8  |   pub struct Body<R: AsyncBufReadPlus + Unpin> {
   |   -------------------------------------------- required by `Body`
...
31 | / fn get_body_reader<'a>(
32 | |     connection: &'a mut dyn AsyncBufReadPlus,
33 | | ) -> Result<Body<&'a mut dyn AsyncBufReadPlus>, ()> {
34 | |
35 | |     Ok(Body::new_fixed_length(connection, 9001))
36 | | }
   | |_^ the trait `AsyncBufReadPlus` is not implemented for `&'a mut (dyn AsyncBufReadPlus + 'a)`

error: aborting due to previous error

I think there are still a few other issues to work out here, but hopefully that can help get you started. I'd probably explore adding an impl<T: AsyncBufReadPlus + ?Sized> AsyncBufReadPlus for &'_ mut T, but I'm not 100% sure that's the right direction to take this - it depends on what exactly you're after.

Well that's quite the example. That's an even more mysterious (to me) error than the first one! I gather there is probably something fundamental that I'm not understanding here. To me,

the trait bound `&'a mut (dyn AsyncBufReadPlus + 'a): AsyncBufReadPlus` is not satisfied

makes no sense; I though trait objects — &dyn T/&mut dyn T, or the long-form &'a mut dyn (T + 'a) which I believe is equivalent to &mut dyn T, just with the lifetimes spelled out — I thought trait objects implemented their trait, trivially/by definition?

impl<T: AsyncBufReadPlus + ?Sized> AsyncBufReadPlus for &'_ mut T

I had never considered such a thing, since I've been thinking this is some sort of given.

But why does it work for AsyncRead? Is there such an impl like the above somewhere for that type? (Is it this one?) And why do those have to be manually specified? (I presume there's some case or something where it would be inappropriate?)

The key is that dyn AsyncBufReadPlus implements AsyncBufReadPlus, but &mut dyn ... doesn't. T and &mut T are distinct types, and it's not 100% obvious that if T implements at type, &mut T does.

For instance, consider

struct MyStruct;
impl Into<String> for MyStruct { ... }
fn x(_: impl Into<String>) { ... }

MyStruct: Into<String> is satisfied, so x(MyStruct) works. But &'_ mut MyStruct: Into<String> isn't, since no one implemented it, so x(&mut MyStruct) doesn't work. And this makes sense, since Into takes MyStruct by value, so you shouldn't be able to call it on &mut MyStruct.

This can be somewhat confusing, mainly because lots of std traits have the implementations like I mentioned. For instance, if I'd used AsRef<str> rather than Into<String>, it would have worked, since there's an impl<T, U> AsRef<U> for &'_ T where T: AsRef<U>.

For an arbitrary trait, though, T: Trait never implies &T: Trait nor &mut T: Trait. They're different types, and thus have a different set of trait impls.

Yep! It has an impl like the above - in the docs, it's here: tokio::io::AsyncRead - Rust.

It has to be manually specified mostly because it leads to a saner type system overall. It might make sense in many situations to have T: Trait imply &T: Trait or &mut T: Trait, but in many others traits represent abstract things (think of things like Send, Sync, Pin and Unpin) which shouldn't automatically be copied over.

The other big issue is what if a trait has a method taking self or &mut self? It can't be auto-implemented for &T, and if taken by value, can't be implemented for T. And it's a guarantee that any trait can add a new method taking self by value, or taking &mut self, as long as that method has a default implementation...

This is a part of a larger pattern I've somewhat noticed, where rust often prioritizes usability for library users, but not really for library writers. There are a few constructs which end up being really verbose, like this, in order to make a nice consistent interface for the people using the traits.

2 Likes

As it has been said in this post, the &mut T -> T delegation is not always trivial. Heck, even in your example, it only works when / because T : Unpin!
So you need to explicitely add it:

#[deny(unconditional_recursion)] // prevent silly mistakes
impl<T> AsyncBufReadPlus for &'_ mut T
where
    T : ?Sized, // include cases such as `T = (dyn AsyncConnection + '_)`
    T : AsyncBufReadPlus,
    T : Unpin, // for the poll_fill_buf impl
{
    fn buffer(&self) -> &[u8]
    {
        T::buffer(&**self)
    }
    fn consume(&mut self, amt: usize)
    {
        T::consume(&mut **self, amt)
    }
    fn poll_fill_buf (self: Pin<&mut Self>, ctx: &mut Context)
      -> Poll<io::Result<&[u8]>>
    {
        T::poll_fill_buf(Pin::new(&mut **self.get_mut()), ctx)
    }
}
1 Like

Another complicating factor for understanding when a trait is implemented is that when you call methods on a reference (& or &mut), Rust can auto-dereference it to get the inner type (if the method doesn't exist on the reference itself). So you probably have written code that calls Trait methods on trait object references (&dyn Trait) and it worked perfectly, not because &dyn Trait implements Trait, but because dyn Trait does.

1 Like

Thank you all! Both the explanation and the example were really useful.

In particular, the counterexample of Into was great; I knew about Into, but I hadn't thought of it in this context.

I did have one question about the example implementation:

    fn poll_fill_buf (self: Pin<&mut Self>, ctx: &mut Context)
      -> Poll<io::Result<&[u8]>>
    {
        T::poll_fill_buf(Pin::new(&mut **self.get_mut()), ctx)
    }

Why is the &mut **self required? Specifically, why does the &mut and one of the * not cancel out / why not *self? And in my testing, *self isn't sufficient; it must but &mut **. The error we get is,

move occurs because value has type `&mut T`, which does not implement the `Copy` trait

Are we just derefing the &mut T to a T and creating a new ref, one that can be moved?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.