Skip to content

Using bare trait where not possible should suggest to use a trait object or generic bound #35825

Closed
@punmechanic

Description

@punmechanic

The following code snippet will generate an E0277 error - I've elided some as it's not entirely necessary for the whole context, but this is not runnable:

type SomeType = /* whatever */;

fn do_something(iter: Iterator<SomeType>) {}

fn get_some_iterator() -> Iterator<SomeType> {
  ...
}

fn main() {
  let iterator = get_some_iterator();
  do_something(iterator);
}

E0277 indicates the following message:

error: the trait bound `std::iter::Iterator<Item=SomeType> + 'static: std::marker::Sized` is not satisfied [E0277]
help: run `rustc --explain E0277` to see a detailed explanation
note: `std::iter::Iterator<Item=SomeType> + 'static` does not have a constant size known at compile-time
note: all local variables must have a statically known size

I want to stress that yes, this error is factually correct: The reason why I have this error is because the naked trait Iterator has no defined size at compile time, and that the solution is to either Box or borrow an Iterator (and thus rely on dynamic dispatch) or convert do_something into a generic function so that it is then monomorphised and Rust is aware of the size of the object at compile time.

My suggestion is that the error message is not entirely clear: While it is true that the error is due to the trait size being undefined, coming from another language (like C#/Java/TypeScript) which uses interfaces, this can be confusing as traits are almost-but-not-quite analogous to interfaces. It may be useful to add an extra note to this error message (or accompanying docs) when passing a bare trait with something along the lines of:

note: You appear to be passing a bare trait object as a parameter. ...

And explain in some basic detail the difference between these approaches. For example:

Passing a trait in this manner results in an undefined size at compile time because the implementation of the Trait is unknown until runtime. You should either place the Trait in a Box or immutable borrow which will enable the type to be resolved at runtime (and use dynamic dispatch) or alter the function to use the Trait as a generic bound (and be monomorphised and use static dispatch).

Note: This behaviour led me to write this article when trying to fully understand the topic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-suggestion-diagnosticsArea: Suggestions generated by the compiler applied by `cargo fix`A-trait-systemArea: Trait systemC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions