Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Named opt-in trait impls #3611

Open
crlf0710 opened this issue Apr 12, 2024 · 2 comments
Open

Named opt-in trait impls #3611

crlf0710 opened this issue Apr 12, 2024 · 2 comments

Comments

@crlf0710
Copy link
Member

We all know that from inference perspective, all impls are "global" if not excluded with coherence. So this is actually part of the "inference API" of a crate. (But not yet well documented by tools like rustdoc to this day, sadly)

I've seen many tragedies that are straightforward outcome of this.

But i feel we should have patching tools that move ourselves forward, if this is not solvable problem in the near future.

What if we could opt-out an impl from the implicit parts of inference?

Strawman proposal: Named opt-in trait impls.

#[optin_impl(u8_idx)]
impl Index<u8> for [T] {
      // impl
}

fn foo1() {
     dbg!([1,2,3][3u8]);    // Doesn't compile, because no impls is selectable
     dbg!([1,2,3][3usize]);    // Works fine as always, with `usize` as idx,
     dbg!([1,2,3][3]);    // Works fine as always, with `usize` as idx,
}

#[use_optin_impls(u8_idx, ...)]
fn foo2() {
     dbg!([1,2,3][3u8]);    // Works fine, with `u8` as idx,
     dbg!([1,2,3][3usize]);    // Works fine, with `usize` as idx,
     dbg!([1,2,3][3]);    // Doesn't compile, because two impls are both in effect     
}
@kennytm
Copy link
Member

kennytm commented May 25, 2024

#3634 is probably the same as this with a different syntax.

mod a {
    pub use impl<T> Index<u8> for [T] {
        ...
    }
}

mod b {
    fn foo2() {
        use ::a::{impl<T> Index<u8> for [T]};
        dbg!([1,2,3][3u8]); // ok
        dbg!([1,2,3][3usize]); // ok
        dbg!([1,2,3][3]); // error[E0277]: the type `[{integer}]` cannot be indexed by `i32`
    }
}

@Tamschi
Copy link

Tamschi commented May 25, 2024

It covers this one in terms of use-cases and the core idea seems pretty similar, yes.
(I think you'd have to write use super::a::… to address the parallel module there.)

More specifically, the example here is roughly Using Scoped Implementations to Implement External Traits on External Types.
Additionally, and while not part of the main RFC, Negative scoped implementations bikesheds some syntax to shadow an implementation without replacement to avoid the ambiguity above.

There are essentially two reasons I didn't attach names to implementations:

  • clear imports
  • the subsetting means broadening such an implementation doesn't have non-local effects

It has a few other benefits though, like less complicated semantics and that it's more natural to still apply all other coherence rules (besides the "orphan rule") even where these implementations are defined, which makes constructing an API a bit less error-prone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants