-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Delegating permissions to implement Trait #2946
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
Comments
See https://github.com/Ixrec/rust-orphan-rules for a more complete summary of why this stuff is the way it is and existing proposals haven't gained much traction. This specific idea of the trait-owning crate allowlisting potential implementer crates usually runs into a "not solving the actual problem" objection like this one: https://internals.rust-lang.org/t/revisit-orphan-rules/7795/59?u=ixrec
|
@lxrec was faster. But here is an example that illustrates why I think this would be hard to do. I think this could work if you only delegate permissions to a single crate. But this kind of defeats the purpose. An IMO you can just as easily use feature flags for this (and might even be more flexible with feature flags). If you allow multiple other crates to implement: What happens if there are actually multiple crates that do implement the trait for your types somewhere in your dependency hierarchy? For example because you have both This issue could probably be avoided for the direct dependencies of your crate, but it gets more complicated (or impossible) for indirect dependencies. E.g. for something like this dependency tree:
In this scenario you wouldn't be able to use both |
The exact same issue exists today with feature flagged implementation of traits. If you have a I would go so far that this is "worse" then what would be introduced with this proposal as the implementer of |
I'd think the serde case should always be handled by optional dependencies, but optional dependencies could be improved dramatically. We could make it far easier use dependencies only when another crate requires them.
I think delegation proposals envision crate authors creating micro subcrates, but doing this seems far less user friendly than the current optional dependencies scheme, not only for the crate developer, but also downstream. In short, the serde example gives a reason why rust should not do this even if it could do so. |
Note that delegation and optional dependencies/automatic features are targeting different kinds of orphan rule pain. AFAIK there's a rough consensus that we probably would like both in some form, but finalizing either of them is not a priority for now. (and as usual, we're just recapping points made a million times in previous threads about this) |
I'm curious how you come to the assessment that it's less user friendly. feature flags are entirely opaque, often the only way to find them is to inspect the On the other hand, dependencies are a much more elaborate system with more capabilities, they're easily explorable, they're (mostly) independent of each other by design, they allow overwriting via the cargo toml if needed. There is an additional burden on releasing more then one new version if a trait fundamentally changes to the level of breaking backwards compatibility. @Ixrec I might be missing something but the discussion you linked was about allowing "random" traits to implement Features on Types, there is a very distinct difference in doing that and allowing explicit delegation of permissions. It's erveryone-has-root vs. there-is-a-sudoer-group. But I probably might miss other discussions on the topic? |
The repo and the IRLO thread I linked both cover a lot of ground (especially if you follow all of the other stuff they link to), including forms of allowlisting as proposing here, abandoning the orphan rules to allow "random" impls, and just about everything else in between. |
Given that you appear to have opened this in response to a tweet about the discoverability and related UX elements of features being terrible, I think it'd be more productive to discuss the root problem (features UX), rather than just taking for granted that this is the best solution. ...and yes, I do agree that Cargo's support for features has terrible UX which encourages building with features you don't need because it makes it so laborious to learn of the existence of and turn off the ones you don't need. Maybe at least something on crates.io, lib.rs, and built into cargo itself that functions similarly to cargo-feature-set... a command, might I add, that I only just learned of now from by accident despite actively searching for something like it multiple times over the last three years... though I still need to open a feature request for an option to have cargo-feature-set hide transitive dependencies I can't affect. (EDIT: Done.) Maybe also some kind of negative feature support in [dependencies.foo]
features = ["-bar"] # ...but keep baz ...instead of something like this: [dependencies.foo]
default-features = false # Disable bar
features = ["baz"] |
Currently, it is only possible to implement traits on types if either:
a. a crate owns the type
b. a crate owns the trait
The reasoning for that, as far as I understand, is that allowing random crates to implement traits on types where they own neither we quickly end up in a situation where there are competing implementations of traits in which rust can't decide anymore which is the "right one".
This can lead to an unpleasant situation for traits that are generally useful where:
A crate implementing a common, useful, widely used, the trait has to depend on and implement traits on a large number of other crates they wish to implement the trait for - usually accompanied by a number of feature flags so users can cut down the dependency graph.
A crate implementing a common type having to depend on and pull in a number of trait-crates to implement their traits. Again usually accompanied by a number of feature flags to cut down the use of unused dependencies.
An example
The probably best example for such a case is serde, which, rightfully so, a lot of crates depend on because it offers an important trait that crates might implement.
At the time of writing, there are over 5000 crates (https://crates.io/crates/serde-json/reverse_dependencies) depending on it which to a large degree, if you just use them for functionality do not require serde at all.
(note this is not a criticism on serde or any library using its traits!)
Proposal
Allowing crates to delegate the right to implement traits on their types, or implement their trait for types to other crates would allow people to opt in / out of dependencies in an independent manner from feature flags.
In other words, a crate
a
could specify a list of other crates (b
,c
,d
) that are allowed to either B, C, D are allowed to implement traits in A on types that are neither owned by A nor themselves or implement traits that neither they nor A own on types owned by A.Since permissions would need to be explicitly delegated by a create that has the permissions themselves this would not break the initial reason of conflicting trait implementations on types.
Cargo.toml (for crate
a
)An example (again):
The user of the
url
crate could delegate permissions to a crateurl-serde
so thaturl-serde
could implement Serialize and Deserialize on the types defined inurl
. This would remove the need for future flags and people wishing to use serialization withurl
can explicitly add the extra dependency.limitations
A limitation with this minimalistic approach would be that derive based traits couldn't be delegated. However, nothing in this proposal would block adding this later on with an additional construct around deriving for foreign structs.
The text was updated successfully, but these errors were encountered: