rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.06k stars 12.54k forks source link

Compiler complains "method cannot be invoked on a trait object" while in fact, a trait bound is missing #78595

Closed jocutajar closed 1 year ago

jocutajar commented 3 years ago

Why is it a problem: I'm so used to helpful compiler advice that I'm almost upset with this misleading statement :)

Actual: Calling StreamExt::forward fails unless I add Unpin trait, but the error suggests that it is not possible to call the method on the trait object because it has to be Sized, no mention of Unpin and on top of it it suggests adding a use clause that is already present.

Expected: An advice about missing Unpin trait.

Reproduce: see example:

use futures::Future;
extern crate futures; // 0.3.6

use crate::futures::StreamExt;
use futures::Sink;
use futures::Stream;

/// Notice missing +Unpin constraint and the misleading error produced
fn test_fails(
    input: Box<dyn Stream<Item = Result<(), ()>>>,
    output: Box<dyn Sink<(), Error = ()>>,
) -> Box<dyn Future<Output = Result<(), ()>>> {
    input.forward(output)
}

fn test_works(
    input: Box<dyn Stream<Item = Result<(), ()>> + Unpin>,
    output: Box<dyn Sink<(), Error = ()> + Unpin>,
) -> Box<dyn Future<Output = Result<(), ()>>> {
    Box::new(input.forward(output))
}

Error:

error: the `forward` method cannot be invoked on a trait object
    --> src/lib.rs:13:11
     |
13   |     input.forward(output)
     |           ^^^^^^^
     | 
    ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:1259:27
     |
1259 |         Self: TryStream + Sized,
     |                           ----- this has a `Sized` requirement
     |
help: another candidate was found in the following trait, perhaps add a `use` for it:
     |
1    | use crate::futures::StreamExt;
     |
rbtcollins commented 3 years ago

Happens for collect, try_collect etc etc as well of course.

szarykott commented 2 years ago

Happened for me in case of map.

DonSheddow commented 1 year ago

I encountered a similar problem when I forgot to import std::io::Read:

// use std::io::Read;

fn make_reader() -> Box<dyn std::io::Read + 'static> {
    let s = "hello world";
    Box::new(s.as_bytes()) 
}   

fn main() { 
    let mut buf = Vec::new();

    make_reader().take(100).read_to_end(&mut buf).unwrap();

    println!("{:?}", buf);
}

playground

which returns error: the `take` method cannot be invoked on a trait object and a confusing message about Sized requirement

estebank commented 1 year ago

Current output for Read:

error: the `take` method cannot be invoked on a trait object
  --> src/main.rs:11:19
   |
11 |     make_reader().take(100).read_to_end(&mut buf).unwrap();
   |                   ^^^^
  --> /rustc/eb26296b556cef10fb713a38f3d16b9886080f26/library/std/src/io/mod.rs:1032:15
   |
   = note: this has a `Sized` requirement
   |
help: another candidate was found in the following trait, perhaps add a `use` for it:
   |
3  + use std::io::Read;
   |

Current output for the original report (not sure if it is still equivalent due to the dependencies maybe drifting since):

error[E0277]: the size for values of type `dyn Stream<Item = Result<(), ()>>` cannot be known at compilation time
    --> src/lib.rs:13:11
     |
13   |     input.forward(output)
     |           ^^^^^^^ doesn't have a size known at compile-time
     |
     = help: the trait `Sized` is not implemented for `dyn Stream<Item = Result<(), ()>>`
note: required by a bound in `futures::StreamExt::forward`
    --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-util-0.3.28/src/stream/stream/mod.rs:1555:27
     |
1552 |     fn forward<S>(self, sink: S) -> Forward<Self, S>
     |        ------- required by a bound in this associated function
...
1555 |         Self: TryStream + Sized,
     |                           ^^^^^ required by this bound in `StreamExt::forward`

error[E0277]: `dyn futures::Sink<(), Error = ()>` cannot be unpinned
    --> src/lib.rs:13:19
     |
13   |     input.forward(output)
     |           ------- ^^^^^^ the trait `Unpin` is not implemented for `dyn futures::Sink<(), Error = ()>`
     |           |
     |           required by a bound introduced by this call
     |
     = note: consider using the `pin!` macro
             consider using `Box::pin` if you need to access the pinned value outside of the current scope
     = help: the trait `futures::Sink<Item>` is implemented for `Box<S>`
     = note: required for `Box<dyn futures::Sink<(), Error = ()>>` to implement `futures::Sink<()>`
note: required by a bound in `futures::StreamExt::forward`
    --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-util-0.3.28/src/stream/stream/mod.rs:1554:12
     |
1552 |     fn forward<S>(self, sink: S) -> Forward<Self, S>
     |        ------- required by a bound in this associated function
1553 |     where
1554 |         S: Sink<Self::Ok, Error = Self::Error>,
     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StreamExt::forward`

error[E0308]: mismatched types
  --> src/lib.rs:13:5
   |
12 | ) -> Box<dyn Future<Output = Result<(), ()>>> {
   |      ---------------------------------------- expected `Box<(dyn futures::Future<Output = Result<(), ()>> + 'static)>` because of return type
13 |     input.forward(output)
   |     ^^^^^^^^^^^^^^^^^^^^^ expected `Box<dyn Future<Output = ...>>`, found `Forward<dyn Stream<Item = ...>, ...>`
   |
   = note: expected struct `Box<(dyn futures::Future<Output = Result<(), ()>> + 'static)>`
              found struct `Forward<dyn Stream<Item = Result<(), ()>>, Box<dyn futures::Sink<(), Error = ()>>>`