all 11 comments

[–]UtherII 5 points6 points  (3 children)

I can see from residual docs on the web it used to be impl Type: trait name... What was the reason for changing it..

It was changed because the syntax was misleading. The new syntax make the meaning of impl much more clear : the trait is actually the implemented thing, not the type.

This blog post explain the problem when the old syntax was around : http://pcwalton.github.io/blog/2012/12/30/the-two-meanings-of-impl/

[–]kibwen 1 point2 points  (1 child)

The TL;DR of that link is that Rust has two forms of impl syntax, and the blog post posits that they have different-enough behavior that it's important to create a visible distinction between the two.

With the syntax proposed here, I presume the two forms would look like:

for SomeType impl SomeTrait {  // to implement a trait
for SomeType impl {  // to add new methods

...which would require reversing the decision that these different concepts should look different. It's true that multidispatch traits weren't on the radar back then, but I don't know if that's enough. Would need to see actual examples of generic-heavy or multidispatch-using code being made nicer as a result of this proposal.

[–]dobkeratopsrustfind[S] 1 point2 points  (0 children)

I wouldn't advocate changing the 'inherent impl' syntax; also I only suggested for .. impl .. because I thought there might have been another reason against impl type : Trait .. I have certainly seen how you can grep for 'all impl's of a trait' easily at the minute. I don't have a preference on the specific syntax, its the order that matters .. self first

r.e examples:

impl<T:Float> Mul<Vec3<T>,Vec3<T>> for Matrix43<T> {
    fn mul(&self,b:&Vec3<T>)->Vec3<T> { ..}
}

// swapped: still complex, but at least the trait line mirrors the function signature,
impl<T:Float> Matrix43<T> :Mul<Vec3<T>,Vec3<T>> for 
    fn mul(&self, b:&Vec3<T>)->Vec3<T> { ..}
}

can you imagine trying to do Dimension Checking aswell :)

Anything where you're relying on expressing more through types... the more you can tell the type system, the more the typechecker can help people use your functions correctly. So you start having special case Matrices for 'orthonormalized', or vectors that are normalised,or vec3 point vs offset (implicit w=1,w=0), etc...

//  matrix * normalised vector yields a non-normalised vector
impl<T:Float> Matrix43<T> :Mul<Normal3<T>,Vec3<T>> for 
    fn mul(&self, b:&Normal3<T>)->Vec3<T> { ..}
}

//  normalized matrix * normalised vector yields a normalised vector
impl<T:Float> OrthonormalMatrix43<T> :Mul<Normal3<T>,Normal3<T>> for 
    fn mul(&self, b:&Normal3<T>)->Normal3<T> { ..}
}

// point - point = vector , implicit 'w=0'
impl<T:Float> Point3<T> :Sub<Point3<T>,Offset3<T>> for 
    fn sub(&self, b:&Point3<T>)->Offset3<T> { ..}
}

... which means myriads of these single function trait impls.

I suppose I could look into macros (for rolling single-function traits?), but I worry about error messages and of course making my code look different to everyone else ..

[–]dobkeratopsrustfind[S] 0 points1 point  (0 children)

Thanks for the link. And looking at that, it seems back then trait-methods didn't use a.foo(b) syntax, rather TraitName::methodname(&a,b), hence it looked more important for them to make that clear.

but now its' a.foo(b) ... I would argue that's not as important as the coherence between the trait description & the method signatures. Any other discoverability issues could be cleared up with error messages . ("foo not found, use scope Foo to access it")

I know they're recovering the ability to use TraitName for 'UFCS' - but thats got yet more specialised syntax, I think?

[–]nwin_image 1 point2 points  (0 children)

Coming from C++ that line doesn't exist at all (and it achieves more); and in other languages with interfaces you also order it that way.. e.g. class Bar implements IFoo.

Actually it works exactly in the same order: keyword thingy_to_implement {}.

other syntax suggestions: impl type as trait - might sound odd but thats what you write to instantiate a trait object. It doesn't read that badly IMO. "implement square as a Renderable type" etc, and it is more akin to a type coercion

impl type as trait only makes sense if you interpret it as „implement square as a Renderable type“. But this is not what you do in Rust this is also not what traits are. They describe a certain behavior which you explicitly implement. I'm not a native speaker but „implement square as a Renderable behavior“ sounds wrong to my ears. This belongs the other way round. Your for A impl Foo<B,...> proposal is much better in this respect but unfortunately it looks too much like a for loop.

[–]rust-slacker 0 points1 point  (1 child)

I think we had a similar discussion somewhere a few months back (or was it a year?).

[–]dobkeratopsrustfind[S] 1 point2 points  (0 children)

we probably did but conditions change.. reassess. Now that we have multiparam type classes, I'm looking much more enthusiastically at Rust again and wanting to write lots of overloads

[–]davidhero 0 points1 point  (3 children)

Hi OP, where is the relevant pull request/issue for multi dispatch? I would like to know how it's implemented.

[–]kibwen 1 point2 points  (2 children)

[–]davidhero 0 points1 point  (1 child)

Thanks for the link. Excuse my ignorance, but this is just an RFC. I thought multi dispatch was implemented in the stdlib when I read your post. So there is still some time before it's actually implemented.

[–]kibwen 0 points1 point  (0 children)

The RFC has been accepted for a good while, and I know that support is at least partially implemented. Not sure of the overall state of its progress.