Variable bindings

The let keyword

let introduces a variable binding:

let x; // declare "x"
x = 42; // assign 42 to "x"

This can also be written as a single line:

let x = 42;

Type annotation

You can specify the variable’s type explicitly with :, that’s a type annotation:

let x: i32; // `i32` is a signed 32-bit integer
x = 42;
// there's i8, i16, i32, i64, i128
//    also u8, u16, u32, u64, u128 for unsigned
This can also be written as a single line:
let x: i32 = 42;

Uninitialized variables

If you declare a name and initialize it later, the compiler will prevent you from using it before it’s initialized.

let x;
foobar(x); // error: borrow of possibly-uninitialized variable: `x`
x = 42;

However, doing this is completely fine:

let x;
x = 42;
foobar(x); // the type of `x` will be inferred from here

Throwing values away

The underscore _ is a special name - or rather, a “lack of name”. It basically means to throw away something:

// this does *nothing* because 42 is a constant
let _ = 42;
// this calls `get_thing` but throws away its result
let _ = get_thing();

Names that start with an underscore are regular names, it’s just that the compiler won’t warn about them being unused:

// we may use `_x` eventually, but our code is a work-in-progress
// and we just wanted to get rid of a compiler warning for now.
let _x = 42;

Shadowing bindings

Separate bindings with the same name can be introduced - you can shadow a variable binding:

let x = 13;
let x = x + 3;
// using `x` after that line only refers to the second `x`,
//
// although the first `x` still exists (it'll be dropped
// when going out of scope), you can no longer refer to it.