Rust is a system programming language that emphasizes safety and performance. If you're new to Rust, understanding its core concepts such as variables, data types, functions, and control flow is essential. In this post, we will explore these foundational elements of Rust, providing examples to help you get started.
Variables in Rust
By default, variables in Rust are immutable(cannot change them after declaration). To make a variable mutable, you need to use the mut
keyword.
Variable Declaration
Here's how you can declare an immutable variable:
let x: i8 = 5;
println!("The value of x is: {x}");
// x = 6; // This line will cause a compile-time error since `x` is immutable.
To declare a mutable variable, use mut
:
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
Constants
Constants are always immutable, and you cannot use the mut
keyword to make them mutable. Constants are defined using the const
keyword and must have their type specified.
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
Shadowing
Shadowing allows you to declare a new variable with the same name as a previous variable. This can be useful for transforming a value without needing to create new variable names.
let x = 5;
let x = x + 1; // This does not throw an error; it creates a new variable `x`.
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}"); // prints 12
}
println!("The value of x is: {x}"); // prints 6
Shadowing can also change the variable's type:
let spaces = " "; // String
let spaces = spaces.len(); // Number
In contrast, using mut
to change the type of a variable results in an error:
let mut spaces = " ";
// spaces = spaces.len(); // This will cause a compile-time error
Data Types
Rust has several basic data types that fall into two categories: scalar and compound.
Scalar Data Types
Scalar types represent a single value. Rust has four primary scalar types:
Integers
Floating-point numbers
Booleans
Characters
Integer Types
Integer types can be signed (i.e., they can be negative) or unsigned (i.e., they cannot be negative). Here are some examples:
i8
,u8
- 8-biti16
,u16
- 16-biti32
,u32
- 32-biti64
,u64
- 64-biti128
,u128
- 128-bitisize
,usize
- architecture-dependent
Integer literals can be written in various forms:
Decimal:
98_222
Hex:
0xff
Octal:
0o77
Binary:
0b1111_0000
Byte (u8 only):
b'A'
Floating-Point Types
Rust has two floating-point types: f32
and f64
:
let x = 2.0; // f64
let y: f32 = 3.0; // f32
Booleans and Characters
Booleans can be either true
or false
:
let f: bool = false;
Characters are specified using single quotes:
let c = 'z';
let z: char = 'ℤ'; // With explicit type annotation
let heart_eyed_cat = '😻';
Numeric Operations
Rust supports standard numeric operations:
// Addition
let sum = 5 + 10;
// Subtraction
let difference = 95.5 - 4.3;
// Multiplication
let product = 4 * 30;
// Division
let quotient = 56.7 / 32.2;
let truncated = -5 / 3; // Results in -1
// Remainder
let remainder = 43 % 5;
Compound Types
Compound types group multiple values into one type. Rust has two primitive compound types: tuples and arrays.
Tuples
Tuples group different types of values:
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {y}");
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
Arrays
Arrays group multiple values of the same type:
let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // [3, 3, 3, 3, 3]
let first = a[0];
let second = a[1];
Accessing an array out of bounds will cause a runtime error.
Functions
Functions in Rust are defined using the fn
keyword:
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {value}{unit_label}");
}
Statements do not return values, so they cannot be assigned to variables:
fn main() {
let x = (let y = 6); // This will cause a compile-time error
}
To return a value from a block, ensure the last expression does not have a semicolon:
fn main() {
let y = {
let x = 3;
x + 1 // No semicolon here
};
println!("The value of y is: {y}");
}
Control Flow
Rust provides several ways to control the flow of your program.
if Expressions
The if
expression allows you to branch your code:
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
Loops
Rust has three kinds of loops: loop
, while
, and for
.
loop
The loop
keyword creates an infinite loop:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break exits loop and returns the value
}
};
println!("The result is {result}"); // 20
}
while
The while
loop runs while a condition is true:
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
for
The for
loop iterates over a collection:
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}
fn main2() {
for number in (1..4).rev() {
println!("{number}!");
}
println!("LIFTOFF!!!");
}
Conclusion
These are the basics of Rust, covering variables, data types, functions, and control flow. Understanding these concepts will provide a strong foundation as you continue to explore the powerful features of Rust. Happy coding!