Variable bindings, Constants & Statics
βοΈ Rust is a statically typed language; it checks data types at compile-time. But it doesnβt require you to actually type it when declaring variable bindings. In that case, the compiler checks the usage and sets a better data type for it. But for constants and statics, you must annotate the type. Types come after a colon(:
).
The naming convention for the variable bindings is using the snake_case
. But, for constants and statics, we should follow the SCREAMING_SNAKE_CASE
.
π In the following examples, we will use data types like
bool
,i32
,i64
andf64
. Don’t worry about them for now; they’ll be discussed later.
Variable bindings
The
let
keyword is used in binding expressions. We can bind a name to a value or a function. Also, because the left-hand side of a let expression is a “pattern”, you can bind multiple names to a set of values or function values.In Rust, variables are immutable by default, so we call them Variable bindings. To make them mutable, the
mut
keyword is used.
let a; // Declaration; without data type
a = 5; // Assignment
let b: i8; // Declaration; with data type
b = 5;
let c = true; // Declaration + assignment; without data type
let d: bool = false; // Declaration + assignment; with data type
let e = 4 + 2; // e = 6
// variable mutability
let mut a = 5; // a = 5
a = a + 5; // a = 10
// multiple variable declaration & assignment
let (a, b) = (1, 2); // a = 1 and b = 2
// multiple variable declaration & assignment with mutability
let (mut x, mut y) = (3, 4); // x = 3 and y = 4
(x, y) = (x-y, x+y); // x = -1 and y = 7
// variable scope
let (x, y) = (1, 2); // x = 1 and y = 2
let z = { x + y }; // z = 3
let z = {
let x = 4; // scope inside {} only
let y = 6; // scope inside {} only
x + y
}; // z = 10
println!("{x} {y} {z}"); // 1 2 10
Constants
The const
keyword is used to define constants and after the assignment their values are not allowed to change. They live for the entire lifetime of a program but has no fixed address in the memory.
const N: i32 = 5;
const DB_PORT: u16 = 5432;
const SERVER_TIMEOUT: u32 = 60 * 5;
Statics
The static
keyword is used to define a “global variable” type facility. There is only one instance for each value, and itβs at a fixed location in memory.
static N: i32 = 5;
static DB_PORT: u16 = 5432;
static SERVER_TIMEOUT: u32 = 60 * 5;
π While you need constants, always use
const
, instead ofstatic
. Itβs pretty rare that you actually want a memory location associated with your constant, and using a const allows for optimizations like constant propagation, not only in your crate but also in downstream crates.
Variable Shadowing
Sometimes, while dealing with data, initially we get them in one unit but need to transform them into another unit for further processing. In this situation, instead of using different variable names, Rust allows us to redeclare the same variable with a different data type and/ or with a different mutability setting. We call this Shadowing.
let s: &str = "hello"; // &str
let s: String = s.to_uppercase(); // String
println!("{s}"); // HELLO
let (a, b) = (1, 2);
let (a, b) = (b, a); // swap variables via shadowing
println!("{a} {b}"); // 2 1
fn main() {
let x: f64 = -20.48; // float
let x: i64 = x.floor() as i64; // int
println!("{x}"); // -21
{
let x = x + 26; // affects inside wrapping {} scope only
println!("{x}"); // 5 π‘ -21 + 26
}
println!("{x}"); // -21 π‘ outer x
}
π¨βπ« Before going to the next…
Usually, constants and statics are placed at the top of the code file, outside the functions (after module imports/
use
declarations).const PI: f64 = 3.14159265359; fn main() { println!("Ο value is {}", PI); }