Rust: Spurious error about Sized bound when trait needs to be imported

Created on 25 Aug 2016  路  8Comments  路  Source: rust-lang/rust

First reported at https://github.com/alexcrichton/futures-rs/issues/97

xx.rs:

use futures::*;

pub fn yy() -> BoxFuture<u32, ::std::io::Error> {
    done(Ok(1)).boxed()
}

main.rs:

extern crate futures;

mod xx;

use futures::Future;

fn main() {
    let f = xx::yy();

    println!("wait: {:?}", f.wait());
}

This code works.

If use futures::Future is commented out in main.rs, compiler complains:

src/main.rs:10:30: 10:34 error: the trait bound `futures::Future<Error=std::io::Error, Item=u32> + Send: std::marker::Sized` is not satisfied [E0277]
src/main.rs:10     println!("wait: {:?}", f.wait());
                                            ^~~~
<std macros>:2:27: 2:58 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
src/main.rs:10:5: 10:38 note: in this expansion of println! (defined in <std macros>)
src/main.rs:10:30: 10:34 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:10:30: 10:34 note: `futures::Future<Error=std::io::Error, Item=u32> + Send` does not have a constant size known at compile-time
error: aborting due to previous error
error: Could not compile `futures-rs-td`.

I'm not sure if is it a bug, or just a usability problem, is it in futures-rs or in rust language, but it is hard to understand error message.

rustc 1.11.0 (9b21dcd6a 2016-08-15), futures-rs from master

cc @stepancheg
cc @jonathandturner
cc @nikomatsakis

A-diagnostics C-bug P-medium T-compiler

Most helpful comment

This is _seriously_ confusing. I just spend 30 minutes trying to figure it out before googling and finding this.

All 8 comments

This is _seriously_ confusing. I just spend 30 minutes trying to figure it out before googling and finding this.

I believe what is happening is this:

  • What we want to do is invoke the _trait method_ <Box<Future> as Future>::wait(&f) (presuming wait() is an &self method)
  • But because the trait is not imported, we don't consider the trait method, and instead look for an _inherent_ method

    • in particular, trait object methods are considered inherent, meaning you don't have to have the trait imported to call them

    • so we wind up converting this (effectively) to <Future as Future>::wait(&*f)

    • this fails because the Self type is Future, and I imagine that wait() is defined with where Self: Sized

Given the popularity of this pattern (e.g., iterators work the same way), it's probably worth trying to tailor an error message for this case if we can.

I imagine something like this:

  • if we are invoked a trait method on a trait object

    • we can check whether the trait method has a where Self: Sized message

The trick is...what do we say in this scenario? Advising to import the trait may not be a good fix, though we could look for trait impls on Box<Trait> and/or &Trait etc and only give the message in that case.

Giving the existing "import the trait" error would at least be an improvement.

Marking as a diagnostics issue. cc @rust-lang/compiler, perhaps we could prioritize this? It's possible that even small wins ("import the trait") here would be very good. Even just almost always doing this might be a lot better if given with the right wording.

OK, so, the challenge here is that method resolution succeeds. I am a bit torn. Perhaps the easiest (and maybe even best) way to solve this is to modify method resolution itself to check for this particular case. That is, when method resolution succeeds, if this is a trait method, we could quickly scan the where-clauses for the method, looking specifically for Self: Sized. If we find it, we can remove it from the list and issue a customized error. I think this is how I would go about it.

I'm out of time this morning for writing detailed mentoring instructions, but let me give a few pointers. I'm happy to elaborate further in the future. The method searching code is found in this module. It works in two phases: the first phase, probing, finds which method applies. That doesn't have to change here, I think.

The second phase, confirmation, does a bit of further work. Here is the implementation of the confirm_method() function. As part of its work, it invokes add_obligations(), which is what records the where clauses (later on, we will check that they are satisfied).

So basically my proposal is to interject there and detect this particular case and issue a custom error.

triage: P-medium

@raphlinus and I ran into this bug this morning when using and_then on a BoxFuture without importing the trait. It took us 10+ minutes working together and replicating a skeleton of our code in play.rust-lang.org and looking for differences before we figured it out.

I think @nikomatsakis's solution would have worked well for us. If it detects you trying to call a trait method with a Self: Sized bound it could jump up a deref-level and use the same logic that failed method resolution normally uses to suggest traits applicable to Box<Future> you might want to import, which would find that futures::Future has an and_then method.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

withoutboats picture withoutboats  路  202Comments

cramertj picture cramertj  路  512Comments

aturon picture aturon  路  417Comments

nikomatsakis picture nikomatsakis  路  331Comments

alexcrichton picture alexcrichton  路  240Comments