9

When declaring a variable of type vector or a hash map in Rust, we do:

let v: Vec<int>
let m: HashMap<int, int>

To instantiate, we need to call new(). However, we do so thusly:

Vec::<int>::new()
   ^^
HashMap::<int, int>::new()
       ^^

Note the sudden appearance of ::. Coming from C++, these are odd. Why do these occur? Does having a leading :: make IDENTIFIER :: < IDENTFIER … easier to parse than IDENTIFIER < IDENTIFIER, which might be construed as a less-than operation? (And thus, this is simply a thing to make the language easier to parse? But if so, why not also do it during type specifications, so as to have the two mirror each other?)

(As Shepmaster notes, often Vec::new() is enough; the type can often be inferred.)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Thanatos
  • 42,585
  • 14
  • 91
  • 146
  • 2
    They aren't called *templates* in Rust; I think your C++ is showing. :-) Anecdotally, I've never seen that particular usage of specifying the type parameters. Using type inference, you can just say `Vec::new()`. The only time I've seen specifying the type like that is for functions that return a trait and you have to pick a concrete type, like `parse` or `collect`. – Shepmaster Mar 10 '15 at 03:33
  • @Shepmaster: Ack, they're generics aren't they? And yes, some amount of type inference can avoid you needing to specify the actual type; I think I stumbled on this mostly as a newcomer to Rust not yet realizing that type inference can do this, and still wonder why. – Thanatos Mar 10 '15 at 03:35
  • I think it's a good question; I didn't even realize that you could specify type parameters at that location! I look forward to an answer, but my guess is that it's going to boil down to simplicity of parsing, as you suggested. I just wanted to comment to indicate that your example code wasn't common, hoping to help other newcomers who read this question. Cheers! – Shepmaster Mar 10 '15 at 03:39
  • In most cases type inference can indeed do the trick, but some functions have type parameters that appear neither in parameters nor in return values, in which case you have to use that syntax. An example of such a function is [std::mem::size_of()](http://doc.rust-lang.org/std/mem/fn.size_of.html) – Vaelden Mar 10 '15 at 17:42

2 Answers2

12

When parsing an expression, it would be ambiguous whether a < was the start of a type parameter list or a less-than operator. Rust always assumes the latter and requires ::< for type parameter lists.

When parsing a type, it's always unambiguously a type parameter list, so ::< is never necessary.

In C++, this ambiguity is kept in the parser, which makes parsing C++ much more difficult than parsing Rust. See here for an explanation why this matters.

Anyway, most of the time in Rust, the types can be inferred and you can just write Vec::new(). Since ::< is usually not needed and is fairly ugly, it makes sense to keep only < in types, rather than making the two syntaxes match up.

Community
  • 1
  • 1
Scott Olson
  • 3,513
  • 24
  • 26
  • Do you know if there was any reason to not also replicate the syntax during variable declaration, something like `let v: Vec::`? – Thanatos Mar 10 '15 at 03:44
  • I made an edit at the end addressing this. I think, basically, `::<` is ugly and we want to avoid it as much as possible. Using `<` in types and having type inference saves us from it most of the time. – Scott Olson Mar 10 '15 at 03:46
  • Is that lexed/parsed as a single symbol `::<`? – Thanatos Mar 10 '15 at 03:51
  • Nope, you could write `:: <` if you wished. – Scott Olson Mar 10 '15 at 03:55
  • I lament, if only rust had followed scala and reserved `[ ]` for generics instead of wasting them on array indexing, which is just as well served with the `( )`s of the `apply` operator. – mako Jan 20 '16 at 02:33
3

The two different syntaxes don't even specify the same type parameters necessarily.

In this example:

let mut map: HashMap<K, V>;

K and V fill the type parameters of the struct HashMap declaration, the type itself.

In this expression:

HashMap::<K, V>::new()

K and V fill the type parameters of the impl block where the method new is defined! The impl block need not have the same, as many, or the same default, type parameters as the type itself.

In this particular case, the struct has the parameters HashMap<K, V, S = RandomState> (3 parameters, 1 defaulted). And the impl block containing ::new() has parameters impl<K, V> (2 parameters, not implemented for arbitrary states).

bluss
  • 12,472
  • 1
  • 49
  • 48