Skip to content

RFC: Allow generic impls using local trait bounds #3821

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

victorGhiglione
Copy link

@victorGhiglione victorGhiglione commented May 27, 2025

This proposal was initially shared in a Reddit post I authored to introduce the idea and gather community feedback:

“Proposal to reconcile generics and Rust’s orphan rule”

While the post is still recent and awaiting responses, it serves as an early exposition of the problem and the motivation behind this RFC.

Rendered


```rust
#[compiler_built_in]
pub trait LocalToThisCrate {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I was expecting this trait was added to every crate with pub(crate) visibility, so foreign crates naturally can't use it, and that the diagnostics can refer to dependency_package::LocalToThisCrate in situation that we are the foreign crate that violated the local-to-their-crate restriction

@comex
Copy link

comex commented May 27, 2025

The example is confusing. What exactly is Calculable? The name makes it sound like a trait but it appears to be a type. It seems like the LocalToThisCrate version would result in not only Vec2 + Point and Point + Vec2 producing Calculable, but also Vec2 + Vec2… is that really what you want?

The error message example also doesn’t make sense. Why would the compiler complain that “T may be a type defined outside of the current crate” when T is an unknown type that’s specifically required to be LocalToThisCrate?

Was this written with the help of AI? The writing style kind of feels like it.

Anyway, this feature doesn’t seem logically unsound, and it might be helpful for some use cases. But I don’t think it would address the most common pain points caused by the orphan rule. Those usually involve wanting to actually impl foreign traits for foreign types, as opposed to just wanting shorthand for writing a bunch of impls for local types.

@victorGhiglione
Copy link
Author

victorGhiglione commented May 27, 2025 via email

@ehuss ehuss added T-lang Relevant to the language team, which will review and decide on the RFC. T-types Relevant to the types team, which will review and decide on the RFC. labels May 27, 2025
@bluebear94
Copy link

I’ll second @comex; the example provided makes the feature look like one with situational utility.

One interesting thing that could be done with LocalToThisCrate is to use it as a supertrait to make the subtrait sealed.

@Jules-Bertholet
Copy link
Contributor

Jules-Bertholet commented May 29, 2025

I recently made a small post on the Internals forum proposing something similar to this, though I had a different use-case in mind. I am glad to see a related idea as a proper RFC.

My internals post doesn’t propose any sort of LocalToThisTrait trait; instead, you define your own trait, and when checking the orphan rules, the compiler verifies when necessary that there are no impls of the trait for upstream types. A major advantage of this is that downstream crates might be able to implement the trait as well.

@Jules-Bertholet
Copy link
Contributor

Jules-Bertholet commented May 29, 2025

I thought using AI would allow for a better translation, but I'm not sure it was the best choice.

A big sign of this is that there is a lot of “fluff” and vague/generic language in the RFC currently, especially in later sections (“Prior Art” and following). An RFC is not a box-checking exercise or something with a minimum/maximum word count. The goal is to provide all the info that the lang team will need when they discuss and decide on the proposal. They are always very busy—too many proposals, too little time—so you want to make it easy for them. If a sentence or paragraph doesn’t contribute to that, cut it out. And if it is an important detail, or something they might have questions about, then make sure it’s explained with concrete examples that demonstrate the full implications.

trait LocalToThisCrate {}
```

## What is `LocalToThisCrate`?
Copy link
Contributor

@kpreid kpreid Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a very important thing which the wording of this RFC doesn’t make clear: there is more than one LocalToThisCrate trait, one for each crate. Questions that came to mind while reading were “why is this a ‘compiler-generated trait’ and not a normal ‘lang item’?” and “wait, how does the trait solver handle a trait where T: LocalToThisCrate has a different answer depending on context” — and in hindsight those have the same answer: they are different traits, so T: ::foo::LocalToThisCrate (not real allowed syntax, probably, but notionally) means “T is defined in crate foo”.

I think that the guide-level and reference-level descriptions should both make it clear that LocalToThisCrate isn't one trait, but a family of traits (one per crate).

## Similar problems and solutions in Rust ecosystem

* **Newtype pattern**: Wrapping types locally to implement external traits is the most common workaround. It is widely used but criticized for verbosity and poor ergonomics.
* **Sealed traits**: Used to restrict implementations to local types or crates, but they require coordination and don’t solve the problem of generic implementations of external traits for generic local types.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This RFC would actually make it easier to implement sealed traits, because LocalToThisCrate can be used exactly like a “trait Sealed” created using the public-in-private trick.

pub trait MyTrait: LocalToThisCrate { ... }

This means that dependent crates can no longer implement MyTrait for their own types. However, they could still implement for a generic trait, if the trait’s bounds permit:

crate foo {
    pub trait PartlySealedTrait<T>: LocalToThisCrate { ... }
    pub trait FullySealedTrait<T: LocalToThisCrate>: LocalToThisCrate { ... }

    pub struct FooStruct { ... }
}

crate bar {
    struct BarStruct { ... }
    impl foo::PartlySealedTrait<BarStruct> for FooStruct { ... }
}

So, this is equally expressive to the public-in-private trick, but slightly more expressive than RFC 3323 restrictions which (reasonably!) only allow picking between fully sealed and not. I think that is worth mentioning as a property this feature has — not a great benefit, but a small one, allowing library authors to dispense with the public-in-private trick and use something that more directly expresses what they mean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC. T-types Relevant to the types team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants