1

From what I know: In C language, the "type" of a variable is bound during compile time and the value of that variable is bound during run time.

For example, in int a = 10;, the type int is bound to the variable a during compile time and the actual value 10 is bound (or assigned) to it during run time.

But in Rust, we have let a = 2;. Here, when does the type (say i32 from any of the integer types in Rust) get bound to a?

I am building a front-end Rust compiler and am currently writing the parser phase. At this point, what type should I assign to these variables?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Rahul Bharadwaj
  • 2,555
  • 2
  • 18
  • 29

1 Answers1

5

Type binding is performed at compile time. This is necessary so that the compiler can emit the proper machine instructions (for example, an x86_64 processor doesn't multiply two i32s the same way it multiplies two i64s).

Many dynamically-typed languages, such as Python or Lua, will carry type information along with the values (not the variables) and dispatch operations at runtime based on the type of each operand. On the other hand, statically-typed languages, such as C or Rust, usually discard most of the type information; it's not necessary because the machine instructions required to perform the operation were emitted directly in the executable (which makes the statically-typed program faster than the comparable dynamically-typed program).

We can demonstrate that type binding is done at compile time by having the compiler tell us about type errors (this is known as type checking). Here's an example:

fn squared(x: f64) -> f64 {
    x * x
}

fn main() {
    let a = 2i32;
    println!("{}", squared(a));
}

Compiling this gives the following output:

error[E0308]: mismatched types
 --> src/main.rs:7:28
  |
7 |     println!("{}", squared(a));
  |                            ^ expected f64, found i32

The Rust compiler can infer the type of many variables based on usage (similar to how auto works in C++). When it can't, it'll give an error. For example:

fn main() {
    let a;
}

gives this output:

error[E0282]: type annotations needed
 --> src/main.rs:2:9
  |
2 |     let a;
  |         ^
  |         |
  |         cannot infer type for `_`
  |         consider giving `a` a type

When the compiler encounters errors, it stops and doesn't produce a runnable executable. Since we don't have an executable form of our program, there is no "run time", so the above happens at compile time.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • It may be worth pointing out that "compile time" is actually composed of *many* steps. Lexing, parsing, type inference, desugaring, inlining, monomorphization, optimization, machine code generation (and probably tens or hundreds of other steps) all occur during "compile time". Knowing that types are assigned at compile time is akin to saying that a drop of water is in the Indian Ocean: it's more specific than "in an ocean", but there's still a lot of space to cover. – Shepmaster Mar 18 '18 at 16:16
  • Ok, so if I'm writing a parser for Rust, then should I put some dummy type for a variable and move ahead? – Rahul Bharadwaj Mar 18 '18 at 16:48
  • 2
    @RahulBharadwaj why not have a look at how rustc itself [does it](https://github.com/rust-lang/rust/blob/master/src/libsyntax/ast.rs#L1578)? You can see it has a special type `Infer` that's made just for that kind of types that are not explicit in the source. Later on, while the compiler starts to checks the types, all the `Infer` get replaced by actual types. – mcarton Mar 18 '18 at 22:58
  • Okay, thank you for the suggestion. I will have a look at it. – Rahul Bharadwaj Mar 19 '18 at 07:49