37

Several times I've used the wrong syntax, such as forgetting to use let in this example:

let closure_annotated = |value: i32| -> i32 {
    temp: i32 = fun(5i32);
    temp + value + 1
};
error[E0658]: type ascription is experimental (see issue #23416)
 --> src/main.rs:3:9
  |
3 |         temp: i32 = fun(5i32);
  |         ^^^^^^^^^

I know that this problem is solved by using let, but what is "type ascription" and what is its use?

I found issue #23416 and the feature gate for type ascription, but I could not understand what "type ascription" is or what is its purpose.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Angel Angel
  • 19,670
  • 29
  • 79
  • 105

1 Answers1

48

Type ascription is the ability to annotate an expression with the type we want it to have. Type ascription in Rust is described in RFC 803.

In some situations, the type of an expression can be ambiguous. For example, this code:

fn main() {
    println!("{:?}", "hello".chars().collect());
}

gives the following error:

error[E0283]: type annotations required: cannot resolve `_: std::iter::FromIterator<char>`
 --> src/main.rs:2:38
  |
2 |     println!("{:?}", "hello".chars().collect());
  |                                      ^^^^^^^

That's because the collect method can return any type that implements the FromIterator trait for the iterator's Item type. With type ascription, one could write:

#![feature(type_ascription)]

fn main() {
    println!("{:?}", "hello".chars().collect(): Vec<char>);
}

Instead of the current (as of Rust 1.33) ways of disambiguating this expression:

fn main() {
    println!("{:?}", "hello".chars().collect::<Vec<char>>());
}

or:

fn main() {
    let vec: Vec<char> = "hello".chars().collect();
    println!("{:?}", vec);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • 5
    An even *better* example is `Into`; because the type parameter there is a parameter of the *trait*, it's *impossible* to annotate an `.into()` method call to specify the type. You have to rewrite the expression to use an annotated function call instead. – DK. Apr 04 '16 at 01:35
  • `That's because the collect method can return any type that implements the FromIterator` -- but why would it return anything other than the type of "chars()"? – Incerteza May 02 '16 at 13:15
  • 3
    You can collect the remaining items of an iterator into a `Vec`, a `BTreeSet`, a `BinaryHeap`, etc. using `FromIterator`. If the resulting collection is generic, its item type will generally follow the iterator's output type. Some implementations of `FromIterator` are only available for some types of iterators, e.g. `String` implements `FromIterator` for `char` iterators and `&str` iterators only. – Francis Gagné May 02 '16 at 22:48
  • Another example (how I encountered the compile-error message) is `for x in ([-1,0,1]:[i32; 3]).iter().cloned() {`, where you need i32 for the signed integer "-1" (otherwise it complains that u32 doesnt have "-" defined). Probably exists a proper way to do this which doesn't require manually specifying i32. For now I will instead define a variable `let a:[i32;3] = [-1, 0, 1];` on the line above and use it instead. Far more ugly, but it works. – felix Oct 21 '19 at 11:35
  • 1
    @felix `[-1i32, 0, 1]` – Francis Gagné Oct 22 '19 at 00:14
  • @Francis Gagné thanks, that works. Any reason why it is valid to do that to any item in the array to affect all others (aside from the fact that it is an array (all items have same type) so the compiler can/must infer the type of the rest as the same. Since it would have been able to infer the same from the "-" if that was the issue, as `let a = -1` works)? Also that answer made me realize `([-1, 0, 1]as[i32;3])` works too! :) ...Which made me wonder, why do numeric literals allow to skip the "as", but other types doesn't (for that matter, why isn't interchanging "as" with ":" valid)? – felix Oct 22 '19 at 05:44
  • 1
    @felix Please [ask new questions](https://stackoverflow.com/questions/ask) (you have two independent questions in your comment), comments aren't made for asking and answering questions. Also, there is a piece of the puzzle missing because integers default to `i32`, so the fact that the compiler inferred `u32` means that it's getting a hint from elsewhere that they should be `u32` instead. – Francis Gagné Oct 23 '19 at 01:14