Variables in Rust - Rust for TypeScript Developers

Table Of Contents

Naming

In Rust, variables are snake cased, while constants are conventially in screaming snake case

const CONSTANT_VALUE: i32 = 2;
let non_constant_value = 32;

Note that variables defined with const must be explicitly typed


Scope

In Rust, variables are block scoped just like variables declared with let and const in TypeScript

fn main() {
  let x = 32;
  {
    let x = 12;
    println!("{x}") // 12
  }
  println!("{x}") // 32
}

Initialization

In TypeScript, the initialization of variables declared with var is hoisted

console.log(x); // undefined
var x = 12;
console.log(x); // 12

The initialization of variables declared with let or const is not hoisted

console.log(x);
let x = 12;
Uncaught ReferenceError: can't access lexical declaration 'x' before initialization

However, they are assigned undefined if not given an initializer

let x;
console.log(x); // undefined

Rust takes the more modern behavior of let and const and takes it one step further. Variables are not assigned a value if not given an initializer

This works

fn main() {
  let x = 12;
  println!("{x}"); // 12
}

This does not

fn main() {
  let x: i32;
  println!("{x}");
}
error[E0381]: used binding `x` isn't initialized
 --> src/main.rs:4:16
  |
2 |     let x: i32;
  |         - binding declared here but left uninitialized
	|
3 |     println!("{x}");
  |                ^ `x` used here but it isn't initialized

Declaration

Variables declared with const in Rust can be global, but variables declared with let cannot

So this works,

const X: i32 = 12;

fn main() {

}

But this does not

let x = 12;

fn main() {

}
error: expected item, found keyword `let`
 --> src/main.rs:1:1
  |
1 | let x = 12;
  | ^^^ consider using `const` or `static` instead of `let` for global variables

In TypeScript, variables declared with var, let, or const can be declared anywhere and aren’t required to be explicitly typed

let x = 12;

Redeclaration

In TypeScript, variables assigned with var can be redeclared locally

var x = 12;
console.log(x); // 12

var x = 32;
console.log(x); // 32

Variables declared with let or const cannot be redeclared locally

const x = 12;
console.log(x);

const x = 32;
console.log(x);
Cannot redeclare block-scoped variable 'x'.

Rust’s const works the same as TypeScript’s in this sense

fn main() {
  const X: i32 = 12;
  const X: i32 = 12;
}
error[E0428]: the name `X` is defined multiple times
 --> src/main.rs:5:3
  |
4 |   const X: i32 = 12;
  |   ------------------ previous definition of the value `X` here
5 |   const X: i32 = 12;
  |   ^^^^^^^^^^^^^^^^^^ `X` redefined here
  |
  = note: `X` must be defined only once in the value namespace of this block

Rust’s let works like var here—it can be redeclared in the same namespace

fn main() {
  let x = 12;
  println!("{x}"); // 12

  let x = 43;
  println!("{x}"); // 43
}

However, variables declared with let can be redeclared with a different type!

fn main() {
  let x = 32;
  println!("{x}");

  let x = "hi";
  println!("{x}");
}

While in TypeScript, variable declarations can’t change type

var x = 1;
var x = 'hi';
Subsequent variable declarations must have the same type.
Variable 'x' must be of type 'number', but here has type 'string'.

Reassignment

In TypeScript, variables declared with const cannot be reassigned

const x = 12;
x = 93;
Cannot assign to 'x' because it is a constant.

Variables declared with var or let can be reassigned

let x = 12;
console.log(x); // 12

x = 32;
console.log(x); // 32

In Rust, variables assigned with both let and const cannot be reassigned

fn main() {
  let x = 12;

  x = 23;
}
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:6:3
  |
4 |   let x = 12;
  |       -
  |       |
  |       first assignment to `x`
  |       help: consider making this binding mutable: `mut x`
5 |
6 |   x = 23;
  |   ^^^^^^ cannot assign twice to immutable variable

Mutability

As the compiler so kindly pointed out, we can use the mut keyword with let to make x mutable

fn main() {
  let mut x = 12;
  println!("{x}"); // 12

  x = 23;
  println!("{x}"); // 23
}

This differs from TypeScript, where objects and arrays are mutable by default

const arr = [];
arr.push(1);

console.log(arr); // [1]

In Rust, this does not compile without the mut keyword

fn main() {
  let vec = Vec::new();
  vec.push(1);
}
error[E0596]: cannot borrow `vec` as mutable, as it is not declared as mutable
 --> src/main.rs:5:3
  |
4 |   let vec = Vec::new();
  |       --- help: consider changing this to be mutable: `mut vec`
5 |   vec.push(1);
  |   ^^^^^^^^^^^ cannot borrow as mutable

Outro

Thank you for reading! Please let me know if you have any suggestions.