Skip to content

refining_impl_trait: Decide whether explicit opt-in is needed #121718

Open
@tmandry

Description

@tmandry

What is refining_impl_trait?

The refining_impl_trait lints detect usages of return-position impl traits in trait signatures which are refined by implementations, meaning the implementation adds information about the return type that is not present in the trait.

Example

pub trait AsDisplay {
    fn as_display(&self) -> impl Display;
}

impl<'s> AsDisplay for &'s str {
    fn as_display(&self) -> Self {
        *self
    }
}

fn main() {
    // users can observe that the return type of
    // `<&str as AsDisplay>::as_display()` is `&str`.
    let x: &str = "".as_display();
}

The above code compiles, but produces a warning:

warning: impl trait in impl method signature does not match trait method signature
 --> src/main.rs:8:29
  |
4 |     fn as_display(&self) -> impl Display;
  |                             ------------ return type from trait method defined here
...
8 |     fn as_display(&self) -> Self {
  |                             ^^^^
  |
  = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
  = note: `#[warn(refining_impl_trait)]` on by default
help: replace the return type so that it matches the trait
  |
8 |     fn as_display(&self) -> impl std::fmt::Display {
  |                             ~~~~~~~~~~~~~~~~~~~~~~

Explanation

Callers of methods for types where the implementation is known are able to observe the types written in the impl signature. This may be intended behavior, but may also lead to implementation details being revealed unintentionally. In particular, it may pose a semver hazard for authors of libraries who do not wish to make stronger guarantees about the types than what is written in the trait signature.

Future possibilities

Add an explicit #[refine] attribute. This would make it easier to opt in to refining on a per-method basis and make the refine check feel more like a builtin feature of the language.

Make refining_impl_trait error-by-default in a future edition. This would mean we are committed to explicit refinement for impl Traits. Since this would happen over an edition, we could choose to go even further and make it a hard error so you could not blanket-allow it in your crate.

Keep all refining_impl_trait warn-by-default.

Make refining_impl_trait_internal allow-by-default, but keep refining_impl_trait_reachable warn-by-default. This would mean that we think semver hazards for publicly-reachable traits in a crate are important enough to lint against. But for uses internal to a crate, opting in is unnecessary overhead for the user.

Make all refining_impl_trait allow-by-default. This would mean that we think most uses of refinement for return-position impl Trait are deliberate and unlikely to cause semver hazards or other unexpected behavior.

Discussion

The language team is collecting feedback about this lint. We would like to know

  • Whether users expect refinement to require an explicit opt-in.
  • Whether users see value in the refinement check to prevent accidental leakage of implementation details.
  • Whether users expect refinement to work the same between publicly-reachable impls and internal-only impls.
  • Whether users would like to see a #[refine] attribute in the future.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-discussionCategory: Discussion or questions that doesn't represent real issues.F-refine`#![feature(refine)]`; RFC #3245T-langRelevant to the language 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