Search code examples
rusttraitstrait-objects

Why can't Box<dyn Trait> be pased to a function with &mut Trait as parameter


I'm sure this has been asked previously but haven't encountered a question that captures the precise scenario here. I have the following code:

let mut pool: Box<dyn redis::aio::ConnectionLike> = <...>
redis::cmd(COMMAND)
    .arg(LIST)
    .arg(value)
    .query_async(&mut pool)
    .await
    .unwrap();

This returns the error:

error[E0277]: the trait bound `std::boxed::Box<dyn redis::aio::ConnectionLike>: redis::aio::ConnectionLike` is not satisfied
   --> svix-server/src/queue/redis.rs:66:30
    |
66  |                 .query_async(&mut pool)
    |                  ----------- ^^^^^^^^^ the trait `redis::aio::ConnectionLike` is not implemented for `std::boxed::Box<dyn redis::aio::ConnectionLike>`
    |                  |
    |                  required by a bound introduced by this call
    |

Question 1 -- Why does the error say the trait is not implemented for Box<dyn redis::aio::ConnectionLike>? Shouldn't it at least say &Box...?

Regardless, if I instead try to pass pool.as_mut() to query_async, I get this error:

error[E0277]: the size for values of type `dyn redis::aio::ConnectionLike` cannot be known at compilation time
   --> svix-server/src/queue/redis.rs:66:30
    |
66  |                 .query_async(pool.as_mut())
    |                  ----------- ^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |                  |
    |                  required by a bound introduced by this call
    |
    = help: the trait `Sized` is not implemented for `dyn redis::aio::ConnectionLike`

Question 2 -- Why is Sized required here? Isn't a reference in Rust always Sized?

Question 3 -- Is there any way to to pass a dyn ConnectionLike to this

The query_async method, for reference looks like this:

#[inline]
#[cfg(feature = "aio")]
pub async fn query_async<C, T: FromRedisValue>(&self, con: &mut C) -> RedisResult<T>
where
    C: crate::aio::ConnectionLike,
{
   ...
}

Solution

  • Question 1 -- Why does the error say the trait is not implemented for Box<dyn redis::aio::ConnectionLike>? Shouldn't it at least say &Box...?

    No, because the reference is not part of the generic type C. Rust doesn't name the entire type, it only names the parts of the two types that don't match. The function accepts &mut C and C is the generic part being matched. The Box type is what is substituted for C, and it doesn't implement this trait.

    Question 2 -- Why is Sized required here? Isn't a reference in Rust always Sized?

    A reference is indeed Sized, but as noted above we are matching the type &mut dyn redis::aio::ConnectionLike against &mut C. This results in C being dyn redis::aio::ConnectionLike.

    In Rust, generic types have the Sized bound by default. You have to explicitly specify the ?Sized bound to relax this, which the author of this function did not do. Therefore, C must implement Sized, and dyn redis::aio::ConnectionLike does not.

    Question 3 -- Is there any way to to pass a dyn ConnectionLike to this

    Possibly, but not directly. You would need to implement a type that wraps either a reference-to-dyn ConnectionLike or a Box<dyn ConnectionLike> and pass that instead. Something like:

    struct BoxedConnectionLike(Box<dyn ConnectionLike>);
    
    impl ConnectionLike for BoxedConnectionLike {
        // Implement proxy functions for the trait.
    }