Pre-RFC: Absolute minimum for in-impl imports

Think of it like function overloading in C++.

If you have this code:

void foobar(int x) {
    // do stuff
}

int main() {
    short x = 0;
    foobar(x);
}

The problem with the above code is, if you include the following header:

// bad_foobar.h

void foobar(short x} {
    launch_nuclear_missiles();
}

The meaning of the original code is changed, even though the code isn't visibly different.

This isn't a major problem in practice, but it's a small footgun.

What if the rules were:

  1. Methods in impl T
  2. Methods in impl trait Trait (inside impl T)
  3. Methods in impl Trait for T where Trait is explicitly imported

And obviously FQCN (fully qualified call notation?) overrides 2 and 3 above, and deals with any potential conflicts with 1.

The lookup behavior of #[inherent] impl Trait for T {} is distinct from whether that's a magic attribute or if there's some other syntax it expands to (e.g. your impl T { impl trait Trait {} }, so let's stick to just discussing the minimal bikeshed of the attribute for now.

That said, it does make sense that a "inherent trait impl" would have resolution strength similar to that of a prelude glob import (as _).

But I don't think it has to actually be stronger than a prelude glob import, I think the same strength is enough, given that it's (roughly) a replacement/improvement on trait preludes for owned types.


That said, Soni's preferred syntax does raise an interesting idea: limiting #[inherent] trait impls to types which you have coherence to impl T on. This limits it to not be used for blanket impls, but I think most cases that would really want to use this are concrete types (upstream extension traits are fine and good being a separate import imho) and are fine with the restriction, and the restriction strongly limits what name collision problems could occur (since it's crate coherence local).

How do you mean? We'd expect it to be slightly weaker than inherent items (aka while they're still inherent, they're also traits, and can be disambiguated with FQCN) but preferred over explicitly imported traits (as those aren't inherent).

And then this can also be used to make the following compile:

mod x {
  pub trait Foo {
    fn foo(&self);
  }
  pub struct Bar;
  pub type Baz = dyn Foo;
  impl Foo for Bar {
    fn foo(&self) { ... }
  }
  impl dyn Foo {
    fn foo(&self) { ... }
  }
}
fn foo() {
  let x: &x::Baz = &Bar;
  x.foo(); // calls impl dyn Foo
  <x::Baz as x::Foo>::foo(x); // calls #[inherent] impl Foo for dyn Foo
}

So this solves a small language wart (dyn Foo containing an inherent impl that matches its Foo trait impl, vs dyn Foo containing an inherent impl of trait Foo).

The currently-posted RFC defines the semantics as expanding #[inherent] impl Foo for Bar to impl Foo for Bar + an impl Bar with methods that forward to the trait impl, so it includes that restriction.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.