8

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
  • 326,504
  • 69
  • 892
  • 1,159
Thanatos
  • 40,566
  • 14
  • 81
  • 139
  • 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

11

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,413
  • 22
  • 25
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
  • 10,836
  • 42
  • 47