Rust comes with a rich set of built-in primitive types, which are mainly divided into two categories: scalar types and compound types.
(1) Scalar Types
A scalar type represents a single standalone value. It includes 6 categories:
- Signed integers:
i8,i16,i32,i64,i128,isize(size matches the system pointer width) - Unsigned integers:
u8,u16,u32,u64,u128,usize(size matches the system pointer width) - Floating-point numbers:
f32,f64 - Character (char): Stores Unicode scalar values (e.g.
'a','α','∞'). Each character occupies 4 bytes. - Boolean (bool): Only two possible values:
trueandfalse - Unit type (): Its only value is the empty tuple
(). Although it looks like a tuple, it holds just one value and is not classified as a compound type.
(2) Compound Types (To be covered later)
Compound types are composed of multiple values:
- Array: Example
[1, 2, 3] - Tuple: Example
(1, true)
1) Ways to Annotate Variable Types
You can explicitly specify the type for any variable. Numeric types additionally support suffix annotation. Default types will be applied if no annotation is present:
- Default for integers:
i32 - Default for floats:
f64
Rust can also automatically infer types based on code context.
2) Variable Types
1. Explicit Type Annotation
Specify the type directly after the variable name with : Type. This approach works for all variables.
fn main() {
let isOpen: bool = true;
let price: f64 = 1.0;
}Code language: JavaScript (javascript)
In the example above, isOpen and price are variables. The syntax : TypeName after a variable explicitly defines its data type.
2. Numeric Suffix Annotation
Append a type suffix to a numeric literal to define its type. This method only applies to numbers.
fn main() {
let an_integer = 5i32;
}Code language: JavaScript (javascript)
Here the variable type is not declared with :. Instead, the suffix i32 on the assigned number marks it as a 32-bit integer.
3. Default Type Rules
The compiler uses default types when no annotation or suffix is added:
- Integer literals → Default to
i32 - Floating-point literals → Default to
f64
fn main() {
let default_float = 3.0; // Automatically f64
let default_integer = 7; // Automatically i32
}Code language: JavaScript (javascript)
4. Contextual Type Inference
The compiler can automatically deduce the actual variable type from subsequent code, so manual annotation is not required.
fn main() {
let mut inferred_type = 12;
inferred_type = 4294967296i64; // Type is inferred as i64 from this line
}Code language: JavaScript (javascript)
Mutable Variables & Variable Properties
1. Mutable Variables
Variables modified with the mut keyword can have their values changed.
fn main() {
let mut mutable = 12;
mutable = 21; // Valid: reassign the value
}Code language: JavaScript (javascript)
2. Immutable Type
Once a variable type is determined, it cannot be changed throughout its lifetime. Assigning a value of a different type will trigger a compilation error.
fn main() {
let mut mutable = 12;
mutable = 21; // Valid: reassign the value
mutable = true; // Error: original type is i32, cannot assign bool
}Code language: JavaScript (javascript)
C:\Users\Jack\Desktop\rustdemo>rustc hello.rs
error[E0308]: mismatched types
--> hello.rs:5:12
|
3 | let mut mutable = 12;
| -- expected due to this value
4 | mutable = 21; // Valid: reassign the value
5 | mutable = true; // Error: original type is i32, cannot assign bool
| ^^^^ expected integer, found `bool`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.Code language: JavaScript (javascript)
Notes
In Rust, variables are immutable by default. You need the mut keyword if you want to modify their values, which differs from many other programming languages.
The code below works in most other languages but produces an error in Rust:
fn main() {
let mutable:i32 = 12;
mutable = 21; // Try to reassign the value
}Code language: JavaScript (javascript)
Compilation error:
C:\Users\Jack\Desktop\rustdemo>rustc hello.rs
error[E0384]: cannot assign twice to immutable variable `mutable`
--> hello.rs:4:2
|
3 | let mutable:i32 = 12;
| ------- first assignment to `mutable`
4 | mutable = 21; // Try to reassign the value
| ^^^^^^^^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
3 | let mut mutable:i32 = 12;
| +++
warning: variable `mutable` is assigned to, but never used
--> hello.rs:3:6
|
3 | let mutable:i32 = 12;
| ^^^^^^^
|
= note: consider using `_mutable` instead
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
warning: value assigned to `mutable` is never read
--> hello.rs:4:2
|
4 | mutable = 21; // Try to reassign the value
| ^^^^^^^^^^^^
|
= help: maybe it is overwritten before being read?
= note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
error: aborting due to 1 previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0384`.Code language: JavaScript (javascript)
3. Variable Shadowing
Defining a new variable with the same name using let will shadow the old one. They are completely separate variables and can have different types.
fn main() {
let mutable = 12;
let mutable = true; // Shadows the previous i32 variable; new type is bool
}Code language: JavaScript (javascript)
Compound Types: Arrays & Tuples
1. Array
- Syntax:
[ElementType; Length]. The type and length are fixed, and all elements must share the same type.
let my_array: [i32; 5] = [1, 2, 3, 4, 5];Code language: JavaScript (javascript)
2. Tuple
- Values are wrapped in
(), and a tuple can hold multiple values of different types.
let my_tuple = (5u32, 1u8, true, -5.04f32);Code language: JavaScript (javascript)