(This is pretty unbaked, but feels concrete enough to ask for feedback on.)
Summary
Allows you to write
pub(crate) impl From<dependency::Type> for crate::Type { ... }
to benefit from having the trait implemented internal to your crate without “leaking” the dependency as public.
Motivation
regex@0.2
accidentally had a public dependency on regex_syntax
due to a From
implementation. In parsing applications, it’s not uncommon for your parsing library to have a Span
representation different from the Span
that you want to expose to users.
Even in non-From
cases, it can be useful to implement traits that would otherwise leak a private dependency for local use only.
Guide-level explanation
If you have a dependency on crate foo
, and that crate exposes a type Foo
, implementing From<Foo>
for your type makes foo
a public dependency of your crate. This means breaking changes to foo
are also breaking changes to your crate, as foo
is part of your public API.
If that From<Foo>
implementation is not meant to be used as public API, you can instead write pub(crate) impl
to scope the trait implementation to your local crate only. Users of your library crate will have no access to the trait implementation, so foo
remains a private dependency, able to be bumped in a semver-compatible manner.
Reference-level explanation
The grammar is changed to take a visibility marker before impl
in impl Trait for Type
. pub
is implied, and pub(crate)
is the only other allowed visibility [that the RFC author has bothered to consider].
The trait implementation is not shared with consumers of the crate as a library. Within the crate, however, the type is treated as implementing the trait. Dependencies of the crate can observe the implementation if the type is passed to them. Because of this, the normal orphan rules must be followed.
The scoped trait implementation is not allowed to be converted to a dyn Trait
trait object or returned as impl Trait
, as that would trivially allow leaking the concrete type’s implementation of the trait to library consumers.
Drawbacks
As all language additions, it adds complexity to the language.
[Further drawbacks TODO]
Rationale and alternatives
- TODO
Prior art
[None?]
Unresolved questions
- This would seem to require preventing the type from being turned into a trait object (or abstract type) in order to enforce that it doesn’t leak. Is this sustainable? Is it actually possible that, assuming orphan rules are followed, that leaking trait implementation this way wouldn’t break the “local” reasoning of the implementation? Leaking the implementation to library users would require the dependency be public anyway, in which case the implementation might as well be public (unless for some reason it’s unstable).
- With further restrictions, could this syntax be used to allow selectively breaking the orphan rules? If nobody else sees you break the rules, is it problematic?