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 foo(x) where foo is a method in scope? We don't allow calling foo() (i.e., with an implicit self), but we do allow calling bar where bar is a function in scope. It seems to me like we should allow it since it is weird to need qualification of an item in an inner scope but not an outer one. OTOH, it means we would get worse error messages where a programmer has omitted an explicit self, since it would just be a "not enough args" error, rather than something more specific.
Issue #8888 is still referenced by the Float trait, which contains unused_self parameters.
I've found a few consequences of the RFC that I haven't seen mentioned before, while trying to design a full implementation:
Resolve wouldn't need to track impls _at all_. This means UFCS reverts the need to force impls to be in the same module with their type.
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, i32::foo would only mean <i32>::foo, so you couldn't use modules like std::i32 at all. For this, I propose a relatively simple solution:
// 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 std::i32 are a few constants which would be better off in the Int trait, once we get associated constants.
I would still like having all those primitives in the prelude, but I guess the situation isn't as dire.
EDIT: std::str has 3 free functions (and a bunch of types/traits), maybe it's a better example of a primitive type with an "associated" module.
Nevermind the label fiddling above, I thought I had a case of backward incompatibility, but it's nothing more than bugs.
Quite a while back, #12566 (self argument destructuring) was closed because it'd be part of UFCS - but the closure referenced #11938 which was closed as a dup of this issue, and it seems to have gotten lost in the shuffle. Any updates?
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;
}
}
(https://github.com/rust-lang/rust/blob/0fa17008e76c0d741ef6a3f391bad6d79eacaff4/src/test/run-pass/self-pat.rs)
Hmm, what still needs to be done here? Anything other than self argument destructuring (see the above two comments).
I stumbled into destructoring self today. It would be nice to see it working.
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
}
<anon>:17:7: 17:22 error: no method named `destructuring` found for type `Foo` in the current scope
<anon>:17 x.destructuring(); // Explodes
^~~~~~~~~~~~~~~
<anon>:17:7: 17:22 note: found defined static methods, maybe a `self` is missing?
<anon>:8:5: 10:6 note: candidate #1 is defined in an impl for the type `Foo`
<anon>:8 fn destructuring(&Foo(u, v): &Self) {
<anon>:9 println!("{}, {}", u, v);
<anon>:10 }
error: aborting due to previous error
playpen: application terminated with error code 101
i.e., it supports destructuring Self in the most literal interpretation of the phrase, but it most certainly does _not_ support destructuring the _invocant_.
@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);
}
@ihrwein
Those are completely different. My Foo::destructuring() takes self by read-only reference, while yours takes it by value - and of course it can be destructured in the body; by then it's a variable like any other. That's not the point.
The point is that UFCS, as far as I can see, was specified as making it such that defining a first parameter of type [&[mut ]]Self as a trait function would permit method-call syntax. It's not destructuring in the argument list that's special either - for anything other than Self it works flawlessly, and even for Self it works when either invoked as a static function or in any parameter but the first.
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:
(In fact, the distinction between static methods and other methods is completely erased, as per the method lookup of RFC PR #48.)
Thus, defining a function that takes a first argument of type [&[mut ]]Self inside an impl block should permit method call syntax, no? But even without destructuring, it fails: http://is.gd/tZKLR9
@eternaleye It refers to self-ish methods behaving like static methods when accessed via a path.
If you read the full RFC, you'll see that it only talks about paths.
Also, if you look at RFC PR 48, there's some interesting discussion about special-casin the first argument, but nothing other than self, &self, &mut self and ~self (now self: Box<Self>) is in the accepted RFC 48.
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.
Under this proposal we would keep these shorthands but also permit any function in a trait to be used as a method, so long as the type of the first parameter is either Self or something derefable Self:
and
fn foo(self: &Self, ...) // equivalent to `fn foo(&self, ...)
and
It would not be required that the first parameter be named self, though it seems like it would be useful to permit it. It's also possible we can simply make self not be a keyword (that would be my personal preference, if we can achieve it).
(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 self keyword can be used as methods"; it tells you what the trait author had in mind, and encourages a more uniform style on users of the trait.
It also means that we can impose a rule that says: "if you use the keyword self, the type must be derefable to Self", rather than us deducing whether the type is derefable to Self. Figuring out _if_ something holds is generally much harder than requiring that it _does_ hold, particularly around generic code.
Regarding conflict, ISTR an older syntax (from before <T as U>::foo()) that may have just been an idea, but was x.Trait::foo()
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 @-binding of the invocant, ugly as that would be :disappointed:
Most helpful comment
Regarding conflict, ISTR an older syntax (from before
<T as U>::foo()) that may have just been an idea, but wasx.Trait::foo()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
@-binding of the invocant, ugly as that would be :disappointed: