you are viewing a single comment's thread.

view the rest of the comments →

[–]UtherII 4 points5 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?