Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRebalancing coherence. #1023
Conversation
quantheory
reviewed
Mar 28, 2015
|
|
||
| ## Summary | ||
|
|
||
| This RFC proposes two rule changes: |
This comment has been minimized.
This comment has been minimized.
quantheory
reviewed
Mar 28, 2015
| When you first define a trait, you must also decide whether that trait | ||
| should have (a) a blanket impls for all `T` and (b) any blanket impls | ||
| over references. These blanket impls cannot be added later without a | ||
| major vesion bump, for fear of breaking downstream clients. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
|
I don't know that I have thought through the implications enough to be strongly for or against, but I do want to point out that without higher-kinded types, it's sometimes hard to work generically over types of references. This encourages library developers to define interfaces that use mainly Question on a different topic: Should |
This comment has been minimized.
This comment has been minimized.
theemathas
commented
Mar 29, 2015
|
I like the general idea, but there are some corner cases, especially with BTW, I believe that the specifics of |
theemathas
reviewed
Mar 29, 2015
| with the following meaning: | ||
|
|
||
| - A `#[fundamental]` type `Foo` is one where implementing a blanket | ||
| impl over `Foo` is a breaking change. As described, `&` and `&mut` are |
This comment has been minimized.
This comment has been minimized.
theemathas
Mar 29, 2015
What exactly is a "blanket impl"? Does something like impl<T> SomeTrait for Foo<T, Bar> {...} count?
theemathas
reviewed
Mar 29, 2015
| behave the same as `&` and `&mut` with respect to coherence. | ||
| - A `#[fundamental]` trait `Foo` is one where adding an impl of `Foo` | ||
| for an existing type is a breaking change. For now, the `Fn` traits | ||
| and `Sized` would be marked fundamental, though we may want to |
This comment has been minimized.
This comment has been minimized.
theemathas
reviewed
Mar 29, 2015
|
|
||
| ### Proposed orphan rules | ||
|
|
||
| Given an impl `impl<P1...Pn> Trait<T1...Tn> for T0`, either `Trait` |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
theemathas
Mar 29, 2015
Does this allow things like impl<A, B, T: SomeTrait<A, B>> ReverseTrait<B, A> for T?
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
Does this allow things like
impl<A, B, T: SomeTrait<A, B>> ReverseTrait<B, A> for T?
That is allowed if ReverseTrait is defined in the current crate, but not otherwise. (As today.)
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
What about
impl<P1...Pn> Trait<T1...Tn> for T0<U1...Un>?
I'm not really sure what you're getting at exactly. In particular, T0 is any type, so it could be things like Box<Foo> and so on as well as simple types like i32.
The main effect of this RFC is to limit the compound types permitted in T0, however. For example, today, something like (Box<LocalType>, i32) would be accepted. Under this RFC, that is not permitted, because tuples are not considered "fundamental". However, Box<LocalType> or &Box<LocalType> or &LocalType is ok, because & and Box are fundamental.
theemathas
reviewed
Mar 29, 2015
| 1. Modify the orphan rules so that impls of remote traits require a | ||
| local type that is either a struct/enum/trait defined in the | ||
| current crate `LT = LocalTypeConstructor<...>` or a reference to a | ||
| local type `LT = ... | < | &mut LT`. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
They are not included in the proposed set of fundamental types.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
Sorry, let me expound. I do not think it is terribly common to need to implement traits for "naked" unsafe pointer types like that. It certainly didn't arise even once in any of the code I looked at. If you wanted to do so, you could make a newtype (which is probably a good idea, because unsafe pointers aren't really a complete abstraction on their own).
theemathas
reviewed
Mar 29, 2015
| This RFC proposes two rule changes: | ||
|
|
||
| 1. Modify the orphan rules so that impls of remote traits require a | ||
| local type that is either a struct/enum/trait defined in the |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
type and use introduce pure aliases and are irrelevant here. This is focusing on the core type system, in which no aliases exist.
theemathas
reviewed
Mar 29, 2015
| 1. At least one type must meet the `LT` pattern defined above. Let | ||
| `Ti` be the first such type. | ||
| 2. No type parameters `P1...Pn` may appear in the type parameters that | ||
| precede `Ti` (that is, `Tj` where `j < i`). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Mar 30, 2015
Author
Contributor
where clauses are not restricted in any way. What is restricted is the use of a where-clauses to permit otherwise overlapping impls. So if you had:
impl<T> Foo for T
where T: SomeTrait<i32>
{ }
impl Foo for SomeType { }this is legal only if SomeType: !SomeTrait<i32>. This RFC requires that this negative constraint meet orphan requirements before we consider it satisfied, to take into account the possibility of future impls being added. Basically that means that SomeType or SomeTrait must be local to the current crate (though if SomeTrait is fundamental, the rules are a bit more lenient).
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
|
Just to answer the question that was asked a few times as to why the specific set of "fundamental" traits was chosen:
That said, I could certainly see some logic for marking all the "overload" traits ( |
This was referenced Mar 30, 2015
This comment has been minimized.
This comment has been minimized.
cristicbz
commented
Mar 30, 2015
|
I noticed in the alternative sections, you talk about specialization and bring up the problem of associated types (brilliant catch by the way). I'm curious about this paragraph (my emphasis):
My use case of specialisation, like you mention earlier in that doc, would be to provide optimised implementations, like your zip example. In that case we would certainly want associated types to match (or at least be covariant). Essentially we want it to be transparent that you're using a specialised impl and adding specialisations should not be a breaking change. I can't think of any of the "numerous trait patterns" that you mention. I'm sure you're actually right, but could you please elaborate for posterity? The specialisation solution would have made me really happy as it seems both less ad-hoc and less restrictive than the solution proposed in the RFC. |
This comment has been minimized.
This comment has been minimized.
theemathas
commented
Mar 31, 2015
|
@nikomatsakis The main reason I asked about the overloading traits is that some operators perform autoderef / autoref (to be precise, PS You still have not answered about "what is a blanket impl". |
nikomatsakis
referenced this pull request
Mar 31, 2015
Merged
Blanket impls for `&F`/`&mut F` where `F: Fn`/`F:FnMut` respectively #23895
aturon
referenced this pull request
Mar 31, 2015
Closed
Tracking issue for Rebalancing Coherence (RFC 1023) #23918
aturon
merged commit 5ba01a5
into
rust-lang:master
Mar 31, 2015
This comment has been minimized.
This comment has been minimized.
|
After much discussion internally, on the internals post, this thread, and the weekly meeting, this RFC has been approved. It is unfortunate to have to move so quickly on a deep change like this, but the existing system of coherence was simply not in a shippable state without it. The design presented here manages to avoid breaking code while not making strong commitments -- the proposed attribute can be left feature-gated, and eventually replaced with an entirely different mechanism or stabilized as-is, depending on our experience. |
nikomatsakis commentedMar 27, 2015
•
edited by mbrubeck
I recently realized that our current trait system contains a forward compatibility hazard concerned with negative reasoning. The TL;DR is that negative reasoning (that is, the compiler saying that "doing X is legal if the trait T is NOT implemented for some type U") can easily make it impossible to add impls of any traits in a backwards compatible way. The most obvious example of negative reasoning are negative trait bounds, which have been proposed in a rather nicely written RFC. However, even without negative bounds, the trait system as currently implemented already has some amount of negative reasoning, in the form of the coherence system.
This RFC is fairly simple proposal that tries to strike a good balance between parent and child crates, in terms of permitting parent crates to expand but also giving child crates lots of freedom to define the impls they need. However, it does involve tightening coherence so it is somewhat more restrictive (the current rules are designed to permit as much as possible in the child crates; but this winds up limiting the parent crates).
Some prior discussion is on this internals thread, although it's mostly me talking to myself.
Rendered view
[edited to link to final rendered version]