-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Disjointness based on associated types. #1672
Closed
withoutboats
wants to merge
1
commit into
rust-lang:master
from
withoutboats:disjoint_associated_types
+100
−0
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
- Feature Name: disjoint_associated_types | ||
- Start Date: 2016-07-10 | ||
- RFC PR: (leave this empty) | ||
- Rust Issue: (leave this empty) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
During coherence checking, when determining if the receivers of two impls are | ||
disjoint, treat bounds with disjoint associated types as mutually exclusive | ||
bounds. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
Consider this set of impls: | ||
|
||
```rust | ||
impl<T> Foo for T where T: Iterator<Item=u64> { } | ||
impl<T> Foo for T where T: Iterator<Item=i64> { } | ||
``` | ||
|
||
Both of these are "blanket impls" - they implement the trait `Foo` for an open | ||
set of types - any type which implements `Iterator`, with the item `u64` in | ||
the first, or the item `i64` in the second. | ||
|
||
Blanket impls are a tricky beast, because often blanket impls will overlap with | ||
other impls of the same trait, because the type being implemented for also | ||
falls into the blanket impl (or could). | ||
|
||
However, these two blanket impls, intuitively, do not overlap. Because you can | ||
only implement `Iterator` once for each type, you cannot have a type which | ||
implements `Iterator` with both of these types. | ||
|
||
Another example of how this could be useful are the return types of functions. | ||
Consider this code: | ||
|
||
```rust | ||
trait Applicator { | ||
fn apply(&mut self, Event) -> io::Result<()>; | ||
} | ||
|
||
fn apply_it<T>(T) -> io::Result<()> where T: Applicator { ... } | ||
|
||
impl<F> Applicator for F where F: Fn(Event) -> io::Result<()> { ... } | ||
|
||
impl<F> Applicator for F where F: Fn(Event) -> () { ... } | ||
``` | ||
|
||
The return type here is an associated type, a function cannot return both | ||
`io::Result<()>` and `()`. I had code a lot like this in a library I was | ||
writing; the idea was that quick and dirty stateless implementations of the | ||
analog trait to `Applicator` could be written as closures, returning either | ||
an `io::Result` or unit. However, these impls are regarded as overlapping by | ||
rustc today. | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
When considering whether two type variables are disjoint, these (informal) | ||
rules prove that they are disjoint: | ||
|
||
1. If they are both concrete types, and they are not the same type (this rule | ||
already exists). | ||
2. If they are both bound by the same trait, and both specify the same | ||
associated type for that trait, and the types they specify are disjoint. | ||
|
||
Additional rules could be added in separate RFCs, such as rules based on a | ||
syntax for mutual exclusion. | ||
|
||
Note that the second rule is recursive. | ||
|
||
|
||
# How will we teach this? | ||
[teach]: #teach | ||
|
||
This will need to be documented in the reference or some other detailed | ||
document, along with the general description of how Rust coherence works. | ||
Otherwise, this doesn't particularly need to be called out separate from other | ||
aspects of the coherence system. | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
This adds more rules to coherence, making it more complicated. However, they | ||
are intuitive rules, which align with the expected behavior of coherence, so | ||
hopefully they will not add to the learning burden. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
Explicit mutual exclusion of bounds ("negative bound syntax") could provide | ||
some, but not all, of the benefits of this feature. | ||
|
||
We could always do nothing and leave coherence as it is. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
None. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a rule that instantiations of generic types with disjoint parameters are disjoint. i.e.
or even
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. We don't have good, consistent language about this, but when I wrote "two type variables" I meant any type variables (really I meant any type, whether an abstract variable or a concrete type, since the first rule only applies to concrete types), whether they be the receiver of the trait or not (I realize the summary is less broad than this, oops).
This is exactly what I had in mind when I said this rule was recursive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any case where
T
is disjoint butFoo<T>
is not disjoint today? If not it seems redundant to re-specify it here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@comex was talking about the transitivity of the disjunction rule, but perhaps we should note that if
T
is disjoint withU
andX
is a type constructor of the kindtype -> type
,X<T>
is disjoint withX<U>
. No reason this couldn't be a roundup to establish and document some of the basic disjunction rules.EDIT: Actually, this may impact how that rule is implemented. Currently I think all disjunction is based on inequality of concrete types (e.g. we can just see that
Foo<Bar<Baz<i32>>
is notFoo<Bar<Baz<bool>>
), but now that rule also needs to take into account type variables that are bound exclusively.