On 01/11/2013 10:50 AM, Niko Matsakis wrote:
Hi,

Right now we use no delimiter at all to separate multiple trait bounds for a single type variable:

fn foo<T: Ord Eq Hash>(...) {...}

Supertraits share the same syntax, so we should consider them together. If or when impls can implement multiple traits they may also need a similar syntax.

trait Foo: Ord Eq Hash { }


Originally we thought to use commas, but inserting commas into the list creates an ambiguity between the comma that separates type parameters, as illustrated here:

fn foo<T: Ord, Eq, Hash, U: Hash, V>(...) {...}

Marijn and I hashed through all kinds of delimeters and failed to find one, and hence he settled on spaces. It seemed like a good idea at the time.

However, over time, I have found that this syntax is very hard for me to parse, visually speaking. Moreover, Patrick recently observed that there is an ambiguity when the trait names are multi-component paths:

fn foo<T: Ord ::Eq>(...) {...}

Does this indicate one bound `Ord::Eq` or two bounds `Ord` and `::Eq`? The current fix for this is to make the tokenizer treat `id::` differently from `id ::` (note the separating space in the latter example). The first is 1 token. The second is 2 tokens. Actually, I think this behavior already existed, and I can't remember why—I think it had something to do with the flexible treatment of keywords that we used to have. @brson, do you remember?

No, but it's the ugliest part of the lexer. Let's make it go away.


Anyway, it was proposed on IRC today that we could do something like this instead:

fn foo<T:Eq>(..) {...} // One bound is the same
fn foo<T:(Ord, Eq, Hash)>(...) {...} // Multiple bounds require parentheses

As an example, this is why the parens are needed:

fn foo<T:(Ord, Eq, Hash), U>(...) {...}

so you _could_ still allow `foo<T: Ord, Eq, Hash>` since there's no ambiguity, but that's more complex that necessary.

Here's how it looks like in a trait:

trait Foo:(Ord, Eq, Hash) { }

The parens aren't necessary here for disambiguation. Would we want them anyway?

With the parens, the colons aren't necessary so you could instead have:

fn foo<T(Eq)>(...) {...}
fn foo<T(Ord, Eq, Hash), U>(...) {...}


I find this visually appealing. It's easier for my eye to read, particularly if the bounds are complicated.

It does contain many new tokens though.


I know it's a syntax change, and we're trying to avoid those, but I thought I'd throw it out for a wider audience to ponder, particularly as it would eliminate a rather surprising whitespace dependency.

One thing to consider is that presumably trait inheritance will eventually make multi-trait bounds less common. It could even replace multi-trait bounds entirely, though I don't think that's a great idea.

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to