-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Implement multidispatch and conditional dispatch. #17669
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
Conversation
These rules should also make it possible to enable unification of type variables with unsized types. |
I guess it's way too late for this, but for me having the choice of method implementation potentially be determined by the type of some random parameter, or even the time-traveling type of some other function the result gets used in, is moving into confusing, C++-like territory, and I don't like it. It's just too indirect and nonlocal. edit: I realize you can basically do this already with a generic function that forwards to a trait, but you have to really work at it. |
It's needed for lots of stuff in the standard library. |
Method dispatch is also not determined by the type of some random parameter: that would be C++-style overloading, which this patch does not implement. Rather the method dispatch is only determined by the type parameters in the trait. It is true that you can't just look at the LHS of I do agree that programmers should avoid adding type parameters to traits unless they know it's needed. This should be a pretty niche feature. |
I'm extremely excited to use this! |
@@ -194,10 +193,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |||
// Tests whether an obligation can be selected or whether an impl can be | |||
// applied to particular types. It skips the "confirmation" step and | |||
// hence completely ignores output type parameters. | |||
// | |||
// The result is "true" if the obliation *may* hold and "false" if |
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.
typo: "obligation"
I'm really excited to see these land as well. |
This is awesome! Multidispatch is one the features (the other being HKT) that I've been so eagerly waiting for! I gave this PR (rebased on top of e434aa1) a test by implementing the Here's the code: use std::vec::MoveItems;
trait IntoIter<I> {
fn into_iter_(self) -> I;
}
impl<T, I: Iterator<T>> IntoIter<I> for I {
fn into_iter_(self) -> I {
self
}
}
impl<T> IntoIter<MoveItems<T>> for Vec<T> {
fn into_iter_(self) -> MoveItems<T> {
self.into_iter()
}
}
fn into_iter<I, S: IntoIter<I>>(iterable: S) -> I {
iterable.into_iter_()
}
fn main() {
let v = vec![0u8, 1, 2];
// Pick one of these three lines
let i: MoveItems<u8> = into_iter(v); // Works
//let i = v.into_iter_(); // ~ Err1
//let i = into_iter(v); // ~ Err2
} And these are the errors:
I think this should work,
Inference failed. The error message seems to hint DST stuff. |
@japaric note that this PR does not yet integrate with method notation. That rules out the first case. I am not sure about the second, I agree it should probably work. |
OK I looked a bit into the example. I see why the code is refusing to infer. You could argue it's a bug but I'm not sure. So what happens is basically that when asked to infer
You might think that it would rule out the This basically just shows why its still useful to have associated types as designated output only types. Inference works better but under conditional dispatch the compiler must still be somewhat conservative. If Sorry if this is confusingly phrased. I'll try to explain it better later. TL;DR -- this patch is working as intended afaict. |
Actually I might be wrong about it working as intended. Let me look at it a bit more. I'm trying to decide if this ought to be a coherence violation. =) |
Ah, of course, not a coherence violation, because |
That makes sense.
Indeed!
I think it was a good explanation. 👍
I was suspecting that. |
attempt to preserve crate concatenation, this is a backwards compatible change. Conflicts: src/librustc/middle/traits/select.rs
the Fn-FnMut-FnOnce hierarchy.
02b7e6c
to
7a07f2a
Compare
A small thought experiment: // Crate A
trait Foo<T> {
fn foo(&self) -> &T;
}
fn use_foo<T, S: Foo<T>>(s: &S) -> &T {
s.foo()
}
// Crate B, uses crate A
struct B;
impl Foo<B> for u8 { ... }
use_foo(0u8) // compiles, has type B
// Crate C, uses crate A
struct C;
impl Foo<C> for u8 { ... }
use_foo(0u8) // compiles, has type C
// Crate D, which uses A, B and C
use_foo(0u8) // does not compile, ambiguity The expression Of course, this doesn't actually violate coherence (as we think of it), but I think it is a new property of the trait system with these changes. |
@aturon yes. this is the impact of not requiring crate concatenability. |
key point with @aturon's example: the way to think of it is that we consider the visible impls when elaborating out the type parameters, but coherence applies to the fully elaborated source. That means that the various calls to |
Thinking a bit more, I realize that I think we could successfully infer the example that @japaric gave. Basically the principle of @aturon's example is that we will infer the only types the user could have written at each point. On that principle, @japaric's example also has exactly one set of types the user could actually write -- despite the fact that a downstream crate could implement Nonetheless, the reason that the code fails to infer is reasonable and valid, and I think that to adjust the code would require a bit of effort. In particular we have to be careful around the interaction of this case and coherence, since coherence has to be more cautious about what could happen in downstream crates. So I'm roughly content with the behavior as is but I do think we could improve the inference in the future with some effort. Might be worth opening a follow-up bug. |
(Note in particular that this would be a backwards compatible extension) |
Opened an issue. I also noted that the precise rule in question has rather crucial and subtle interactions, and changing it would be tricky. |
Implement multidispatch and conditional dispatch. Because we do not attempt to preserve crate concatenation, this is a backwards compatible change. This is not yet fully integrated into method dispatch, so "UFCS"-style wrappers must be used to take advantage of the new features (see the run-pass tests). cc #17307 (multidispatch) cc #5527 (trait reform -- conditional dispatch) Because we no longer preserve crate concatenability, this deviates slightly from what was specified in the RFC. The motivation for this change is described in [this blog post](http://smallcultfollowing.com/babysteps/blog/2014/09/30/multi-and-conditional-dispatch-in-traits/). I will post an amendment to the RFC in due course but do not anticipate great controversy on this point -- particularly as the RFCs more important features (e.g., conditional dispatch) just don't work without the change.
Implement multidispatch and conditional dispatch. Because we do not attempt to preserve crate concatenation, this is a backwards compatible change. This is not yet fully integrated into method dispatch, so "UFCS"-style wrappers must be used to take advantage of the new features (see the run-pass tests).
cc #17307 (multidispatch)
cc #5527 (trait reform -- conditional dispatch)
Because we no longer preserve crate concatenability, this deviates slightly from what was specified in the RFC. The motivation for this change is described in this blog post. I will post an amendment to the RFC in due course but do not anticipate great controversy on this point -- particularly as the RFCs more important features (e.g., conditional dispatch) just don't work without the change.