Skip to main content

Variable assignment

In this we will assign variable.

src/02-variables/main.rs
fn main() {
let age = 34; // by default variables are immutable
println!("I'm {} years old.", age);

// need to explicitly declare mutable variables with keyword mut
let mut x = 10;
println!("My variable = {}", x);

x = 10 + 5; // rust will automatically try to infer the type from assignment
// explicit type declaration:
// let x: i32 = 5;
// let y = 32_u8;
println!("My variable = {}", x);
type_of(&x); // this is i32

let a = 5;
let b = 6;
type_of(&a); // type of `a` and `b` will be set as u8 because of the
// following statement
let c: u8 = a + b; // rust compiler is smart enough to convert previous
// assignments data types
type_of(&c);

const PI: f64 = 3.14159; // mut keyword is not allowed with const
// cannot be shadowed
println!("Pi * 2 = {}", PI * 2.0); // int cannot be multiplied by float

// variable shadowing
let spaces = " ";

// it is allowed to declare variables with same name shadowing the previous
// variable with the same name
let spaces = spaces.len(); // notice data type has changed too

{
let spaces = spaces * 2;
println!("double spaces length (inner scope) = {spaces}");
}

println!("spaces length (outer scope) = {spaces}");

// if a variable is declared but never used, use `_` prefix to hide warnings
// we can just use `_` naming for throwaway variable (note that isolated `_`
// cannot be used in any expression while `_name` can be)

// underscores in numeric literals for better readability
// can be used for int, float, hexadecimal, and other formats
// let binary = 0b_0110_1001_1111_0001;
println!("2 million: {}", 2_000_000);

// static declaration (not used extensively)
// type declaration is must for static
// static values are always immutable
// uses static allocation (in contrast to stack allocation)
// style guide: use capital letters for static variables (separated with
// underscores)
static NO_WEEK_DAYS: u8 = 7;
println!("No of days in a week = {}", NO_WEEK_DAYS);
}

// helper function to print type of a variable
fn type_of<T>(_: &T) {
println!("Type: {}", std::any::type_name::<T>())
}

String

src/02-string/main.rs
fn main() {
// string literal: they are hard coded into the compiled binary, must be
// known compile time.
let mut message = "Hello";
println!("message: {}", message);

message = "Hello rust";
println!("message: {}", message);

let mut planet = String::from("Earth");
// this assignment is different from string literal
// String type stored in the heap and its size can be dynamically changed
// a corresponding pointer, length, and capacity is stored in the stack
// heap memory is requested to the memory allocator during runtime

planet.push_str(" has one moon.");
// if the heap needs to move the message to a new location in order to
// accommodate the new size, it will also update the pointer address

println!("{planet}");
// once the variable that owns the heap memory goes out of scope, the
// allocated memory is freed and return to the operating system/ allocator.
// this is different from C and C++ where the developer has to explicitly
// free/ delete the allocated memory, or Java where garbage collector looks
// for unused memory and frees it periodically

// in newer C++ standard, similar pattern (Resource Acquisition Is
// Initialization) is implemented (see, unique_pointer)

let x: i32 = 5;
let mut y: i32 = x; // value of x copied to y
println!("x = {}, y = {}", x, y);

y = 10;
println!("x = {}, y = {}", x, y);

// now look at what happens for a data that is stored in the heap
let message = String::from("hello");
let mut alt_message = message; // value of message is not copied here
// the variable points to the same heap
// location, we copy the information stored
// in the stack i.e., pointer, length and
// capacity to the new variable

// after this statement the message variable goes out of scope, it is to
// guarantee memory safety, otherwise there could be double drop
// (deallocation) calls

// this is therefore move, not even a shallow copy, the previous variable
// is invalid hereafter

// Important: passing arguments to a function works the same way as
// assigning variable
alt_message.push_str(" world.");

// println!("message: {}", message); // this will cause compile error
println!("alt message: {}", alt_message);

// in case we do want to make deep copy we can use clone method
let mut alt_message2 = alt_message.clone();
alt_message2.push_str(" Have a great day.");

println!("alt message again: {}", alt_message);
println!("alt message 2: {}", alt_message2);
}