-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Implement UFCS (Tracking RFC 132) #16293
Comments
This is (expected to be a) backwards-compatible change, so while we do desire it, it is not a strict necessity for 1.0 Assigning P-high, not 1.0 milestone. |
Question: in a trait or impl, should it be possible to call |
Part of UFCS (rust-lang#16293)
With the 'receiver' as an argument and static dispatch. Part of UFCS implementation (#16293). r?
Issue #8888 is still referenced by the Float trait, which contains |
Working towards #16293, this adds support for `<T as Trait>::method` in expressions.
I've found a few consequences of the RFC that I haven't seen mentioned before, while trying to design a full implementation: struct Foo<T>;
mod bar {
impl super::Foo<()> { // impl allowed outside of Foo's module.
fn bar(&self) -> Option<()> { None }
}
}
impl Foo<bool> {
// same method name, allowed only because Self cannot overlap
// with any other impl with the same method (as with trait impls).
fn bar(&self) -> Option<bool> { Some(true) }
}
// anonymous impls would be checked by coherence as if they were
// implementing traits from outside the current crate.
impl<T> [Foo<T>] {
fn extra(&self) -> usize { 0 }
}
impl<T> Vec<Foo<T>> {
fn extra(&self) -> usize { self.capacity() - self.len() }
} All of these combined will end up removing some extension traits, and make type aliases much more powerful, without special-casing them. This will be possible, given we complete defaulted type params: // std::collections (the collections crate wouldn't have a clue about RNGs):
type HashMap<K, V, H = RandomizedSipHash> = collections_crate::HashMap<K, V, H>; On the other hand, // in libcore:
#[lang="i32"]
enum i32 {} // unihabited enum introduces a type in scope and nothing else
// coherence would allow this impl as the type is "rooted" in the crate
// defining the lang item.
impl i32 {
fn foo(self) -> ... {...}
} That would also make the rustdoc special handling for primitives a bit simpler. Then again, that last one isn't necessary. I just checked and the only items in EDIT: |
Nevermind the label fiddling above, I thought I had a case of backward incompatibility, but it's nothing more than bugs. |
Adds `<module::Type>::method` support and makes `module::Type::method` a shorthand for it. This is most of #16293, except that chaining multiple associated types is not yet supported. It also fixes #22563 as `impl`s are no longer treated as modules in resolve. Unfortunately, this is still a *[breaking-change]*: * If you used a global path to a primitive type, i.e. `::bool`, `::i32` etc. - that was a bug I had to fix. Solution: remove the leading `::`. * If you passed explicit `impl`-side type parameters to an inherent method, e.g.: ```rust struct Foo<T>(T); impl<A, B> Foo<(A, B)> { fn pair(a: A, b: B) -> Foo<(A, B)> { Foo((a, b)) } } Foo::<A, B>::pair(a, b) // Now that is sugar for: <Foo<A, B>>::pair(a, b) // Which isn't valid because `Foo` has only one type parameter. // Solution: replace with: Foo::<(A, B)>::pair(a, b) // And, if possible, remove the explicit type param entirely: Foo::pair(a, b) ``` * If you used the `QPath`-related `AstBuilder` methods @hugwijst added in #21943. The methods still exist, but `QPath` was replaced by `QSelf`, with the actual path stored separately. Solution: unpack the pair returned by `cx.qpath` to get the two arguments for `cx.expr_qpath`.
Seems that self-argument destructing is not implemented on UFCS. struct A(uint);
impl A {
fn b(self: &A(ref mut u), i: uint) { //~ error: expected identifier, found keyword `ref`
//~^ error: expected one of `(`, `+`, `,`, `::`, or `<`, found `mut`
*u = i;
}
} |
Hmm, what still needs to be done here? Anything other than self argument destructuring (see the above two comments). |
I hit an other problem, it works fine. |
@ihrwein, It still does not work in my testing: struct Foo(i32, i64);
impl Foo {
fn classic(&self) {
println!("{}, {}", self.0, self.1);
}
fn destructuring(&Foo(u, v): &Self) {
println!("{}, {}", u, v);
}
}
fn main() {
let x = Foo(3, 14);
x.classic(); // Works, but doesn't destructure
Foo::destructuring(&x); // Works, but x is not an invocant
x.destructuring(); // Explodes
}
i.e., it supports destructuring |
@eternaleye try this way: struct Foo(i32, i64);
impl Foo {
fn classic(&self) {
println!("{}, {}", self.0, self.1);
}
fn destructuring(self) {
let Foo(u, v) = self;
println!("{}, {}", u, v);
}
}
fn main() {
let x = Foo(3, 14);
x.classic(); // Works, but doesn't destructure
Foo::destructuring(x);
} |
Those are completely different. My The point is that UFCS, as far as I can see, was specified as making it such that defining a first parameter of type The problem is that the method-call syntax is being forbidden when it should be allowed - i.e., exactly UFCS. One use case where this is especially nice, for its pure concision: Implementing state machines where each state is a newtype. |
I believe this has all long since been implemented, so closing. |
@alexcrichton: No, my example is still broken (destructuring the invocant): http://is.gd/3i9oCg |
@eternaleye The accepted RFC supports nothing of the sort. |
@eddyb: From the opening paragraph:
Thus, defining a function that takes a first argument of type |
@eternaleye It refers to
EDIT: I had missed "Receiver reconciliation" which was never fully implemented AFAICT. |
Mm, I suppose I misunderstood then. I'll see about writing up an RFC myself. EDIT: Er, on reading RFC 48 in the repo, what I'm seeing does not match what you say.
and
and
(emphasis mine) |
@eternaleye it's true that we initially intended to not make the name significant, but I think that ship has sailed (and it's worth amending the RFC). For one thing, it would be backwards incompatible to make a change here, as it would introduce potential new ambiguities into method dispatch (though I don't know whether this would actually affect any crates in practice). But also, I've shifted my opinion as to what I think is best. I think it is actively useful to be able to exclude functions from being used as methods, because it allows you to avoid potential conflicts between traits (any number of traits can have associated fns with the same name, but if method notation forces those names to be in conflict if a single type implements all the traits). I also think there is just a certain amount of clarity in a rule like "associated fns whose first argument uses the It also means that we can impose a rule that says: "if you use the keyword |
Regarding conflict, ISTR an older syntax (from before And personally, my interest in this is for cleanliness and concision in implementing things like state machines - in particular, being able to destructure the invocant lends itself very nicely to simple, concise implementations of state machines that can be used in a method-chaining notation. I can certainly see where you're coming from regarding deciding whether the type is derefable to Self, though. Given the above, perhaps I'll do up an RFC for permitting |
https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
The text was updated successfully, but these errors were encountered: