Table of contents
- What is Rust?
- To start, You'll need to install Rust
- Getting Started with "Hello, World! "
- Organizing a Rust Project :
- Basic Formatting
- Placeholder for debug trait
- Printing style
- println!()
- eprint!()
- eprintln!()
- Types of Comments in Rust
- Line Comments //
- Block Comments /*...*/
- Doc Comments
- Outer Doc Comments ///
- Inner Doc Comments //!
- Variables
- Create a Variable
- Initialize a Variable
- What if You Want to Make a Variable Mutable?
- Numeric Types - Integers and Floats
- Variable Size Types
- Implicit Definition
- Floating Point
- Implicit Definition:
- Boolean
- Character and String
- Example
- String
- Example
- Implicit Definition
- Arrays
- Define an Array
- Example
- How to Make a Tuple Mutable?
- Print the Tuple
- What Are Constant Variables?
- Difference Between const and let Variables
- Introduction to Operators
- Assignment and Compound Assignment Operators
- Type
- Borrowing and Dereferencing Operators
- Types
- Example
- Type
- Example
- Precedence and Associativity
- Precedence
- Left to Right Associativity
- Example 1
- Example 2
- If Expression
- If Expression
- llustration
- If…else Expression
- if…else if…else Expression
- llustration
- illustration
- Shorthand if
- Examples
- Case 3: When the Pattern is Replaced With _
- Match Expression
- What Is a match Expression?
- Method 2:
- Use match Statement
- Use if let Statement
- What Is a Loop?
- Types of Loops
- Definite Loop - For Loop
- What Is a for Loop?
- Syntax
- Example
- Example
What is Rust?
Rust is a language empowering everyone to build reliable and efficient software. rust created by Graydon Hoare and many others around 2006 while Hoare was working at Mozilla Research. It gained enough interest and users that by 2010 Mozilla had sponsored the development efforts
https://survey.stackoverflow.co/2022/#technology-most-loved-dreaded-and-wanted
StackOverflow 2022 - it's the seventh year as the most loved programing language with 87% of developers saying they want to continue using it.
the language is syntactically similar to C, so you'll find things like for loops, semicolon-terminated statements and curly braces denoting block structures, Rust can guarantee memory safety through the use of a borrow checker that tracks which part of a program has safe access to a different part of memory. This track does not come at the expense of performance, though. Rust programs compile to native binaries and often match or beat the speed of programs written in c or c++ for this reason, Rust is often described as a system programing language that has been designed for performance and safety.
To start, You'll need to install Rust
one of the easy ways to install, upgrade, and manage Rust using
the rustup tool .it works equally well on Windows, Linux or macOS.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
if you have already installed rustup
, you might want to run rustup update
to get the latest version of the language and tools, as Rust updates in about six weeks. execute rustup doc
to read volumes of documentation. you can check your version of rustc
compiler with the following command.
rustc --version
rustc 1.68.0-nightly (b70baa4f9 2022-12-14)
Getting Started with "Hello, World! "
It's universal truth everybody starts their journey with the hello world program.
create hello.rs
file with the following content :
fn main(){
println!("hello,world!");
}
functions are defined using fn the name of this function in main.
println
is a micro and will print txt to STDOUT.the body of the program is enclosed in curly braces.
rust will automatically start in the main function. function parameter appears inside the parentheses that follow the name of the function. because there is no argument lists in main()
. the function takes no arguments.
to run this program, you must first use rust compiler, rustc
to compile the code.
$ rustc hello.rs
on macOS, you can use the file command to see what kind of file this is
rustlabs file hello
hello: Mach-O 64-bit executable arm64
you can execute your program like this on mac
rustlabs ./hello
hello,world!
The dot(.) indicates the current directory
you can execute your program like this if your using windows
rustlabs ./hello.exe
hello,world!
🍾 cheers!
quiz
- What is the keyword for declaring a function?
fun
function
func
fn
- What is the output of the following code?
fn main() {
println!("Hello World!")
println!("Hello");
}
a) Hello World! Hello b) error hint:- A semicolon can be omitted at the end of the last statement only.
Organizing a Rust Project :
for any project, we all write multiple source files and execute them as a single binary. let's remove our existing hello binary rm hello
let's create a parent directory
$ mkdir -p hello/src
now move the hello.rs
source file into hello/src
using the mv command
$ mv hello
$ rustc src/hello.rs
check the content of the directory using tree
command :
➜ rustlabs tree
.
└── hello
└── src
└── hello.rs
Basic Formatting
A single placeholder is used when it is required to print a single value.
fn main() {
println!("Number: {}", 1);
}
We can use multiple placeholders within the println!()
macro. The number of placeholders should be equal to the number of values to be printed.
fn main() {
println!("{} is a {} Community", "Join", "CloudNativeFolks");
}
Positional arguments specify the positions of the values in a sentence.
fn main() {
println!("Enhance your coding skills from {0} Track. {0} by {1}", "Rustlabs ", " CloudNativeFolks");
}
A placeholder can take a named argument and assign it a value explicitly. It does this by specifying a name within the placeholder and assigning that name the value to be inserted in the string.
fn main() {
println!("{workshop} by {Community} ", Workshop = "Rust", Commmunity = "CloudNativeFolks");
}
If we want to convert the value to binary, hexadecimal, or octal write:
{:b},{:x},{:o}
In the placeholder for binary, hexadecimal, or octal respectively and in the value specify the number. You can use all three of them or one of them in a single expression.
fn main() {
println!("Number : 10 \nBinary:{:b} Hexadecimal:{:x} Octal:{:o}", 10, 10, 10);
}
If we want to convert the value to binary, hexadecimal, or octal write:
{:b},{:x},{:o}
In the placeholder for binary, hexadecimal, or octal respectively and in the value specify the number. You can use all three of them or one of them in a single expression.
fn main() {
println!("Number : 10 \nBinary:{:b} Hexadecimal:{:x} Octal:{:o}", 10, 10, 10);
}
We can perform basic math and the placeholder gets replaced with the result.
fn main() {
println!("{} + {} = {}",10, 10, 10 + 10);
}
Placeholder for debug trait
fn main() {
println!("{:?}", ("This is a Rustlabs", 101));
}
Printing style
The table below summarizes the macros used to print in Rust.
macro | Printing Style |
print!() | prints strings to console |
println!() | same as print!() but also appends a new line character at end of the string |
eprint!() | prints anything within the parentheses as an error |
eprintln!() | same as eprint!() but also appends a new line character at the end |
Let’s discuss each of the macros in detail.
The print!()
the macro simply prints the output to the console. The following example prints “RustLabs by CloudNativeFolks Community ” in one line
fn main() {
print!("RustLab ");
print!(" by CloudNativeFolks Community ");
}
println!()
The println!()
macro appends a new line at the end of the string.
The following example prints “RustLabs” on one line and “by CloudNativeFolks Community” on the new line.
fn main() {
println!("RustLabs");
println!("by CloudNativeFolks Community");
}
eprint!()
The eprint!()
macro displays the output as an error.
The following example prints “RustLabs” and “by CloudNativeFolks Community” on the same line but as an error.
fn main() {
eprint!("RustLabs);
eprint!("by CloudNativeFolks Community");
}
eprintln!()
The eprint!()
macro displays the output as an error and appends a new line(\n) at the end of it.
The following example prints “RustLabs” as an error and appends a new line to it. Then prints “by CloudNativeFolks Community” and appends a new line.
fn main() {
eprintln!("RustLabs");
eprintln!("by CloudNativeFolks Community");
}
📝Note: eprint!()
and eprintln!()
come in handy when we want to indicate to the user that an error condition has occurred.
Types of Comments in Rust
Rust has two types of comments that are commonly used:
Line Comments //
The comments followed by a double forward slash, //
, are known as line comments.
This is the most recommended style of comment for commenting a single line.
// Writing a Rust program
fn main() {
//The line comment is the recommended comment style
println!("This is a line comment!"); // print hello World to the screen
}
Block Comments /*...*/
Text enclosed within /*...*/
is known as a block comment.
This is used for temporarily disabling a large chunk of code.
Doc Comments
The comments followed by ///
or //!
are known as doc comments. Let’s see the difference between the two sets of doc comments.
Outer Doc Comments ///
Outer doc comments are written outside the block of code.
This type of comment supports markdown notation. This is used to generate docs referring to the following item.
Inner Doc Comments //!
Inner doc comments are written inside the block of code.
This comment style is used to generate docs referring to the item within a block of code.
/// This is a Doc comment outside the function
/// Generate docs for the following item.
/// This shows my code outside a module or a function
fn main() {
//! This a doc comment that is inside the function
//! This comment shows my code inside a module or a function
//! Generate docs for the enclosing item
println!("{} can support {} notation","Doc comment","markdown");
}
Variables
A variable is like a storage box paired with an associated name which contains data. The associated name is the identifier and the data that goes inside the variable is the value. They are immutable by default, meaning, you cannot reassign value to them.
Create a Variable
To create a variable, use the let binding followed by the variable name.
What is binding?
Rust refers to declarations as bindings as they bind a name at the time of creation. let is a kind of declaration statement.Naming Convention: By convention, you would write a variable name in a snake_case i.e., - All letters should be lower case. - All words should be separated using an underscore
( _ )
.
Initialize a Variable
A variable can be initialized by assigning a value to it when it is declared. The value is said to be bound to that variable.
Note: It’s possible to declare the variable first and assign it a value later. However, it is not recommended to do this as it may lead to the use of uninitialized variables.
The example below declares a variable, language, and initializes it with a value, Rust, and then displays the value of a said variable:
fn main() {
let language = "Rust"; // define a variable
println!("Language: {}", language); // print the variable
}
What if You Want to Make a Variable Mutable?
At the beginning of this lesson, it was mentioned that a variable is immutable until you want to make a change in the variable, then it can be made mutable. To make a variable mutable, write let followed by the mut
keyword and the variable name:
fn main() {
let mut language = "Rust"; // define a mutable variable
println!("Language: {}", language); // print the variable
language = "Java"; // update the variable
println!("Language: {}", language); // print the updated value of variable
}
Assigning Multiple Variables
It is possible to assign multiple variables in one statement.
fn main() {
let (language,community) =("Rust","CloudNativeFolks"); // assign multiple values
println!("This is a {} labs by {}.", language ,CloudNativefolks ); // print the value
}
Note: If a variable is kept un-assigned or unused, you’ll get a warning. To remove such a warning write the expression #[allow(unused_variables, unused_mut)]
at the start of the program code. However, it’s not a good practice to keep unassigned/unused variables.
Scope and Shadowing
The scope of a variable refers to the visibility of a variable, or , which parts of a program can access that variable.
It all depends on where this variable is being declared. If it is declared inside any curly braces {}
, i.e., a block of code, its scope is restricted within the braces, otherwise, the scope is global.
Types of Variables
There are two types of variables in terms of scope.
- Local Variable
A variable that is within a block of code, { }
, that cannot be accessed outside that block is a local variable. After the closing curly brace, } , the variable is freed and memory for the variable is deallocated.
- Global Variable
The variables that are declared outside any block of code and can be accessed within any subsequent blocks are known as global variables.
The variables declared using the const keyword can be declared in local as well as a global scope.
Note: The following code gives an error, ❌, since the variable created inside the inner block of code has been accessed outside its scope.
fn main() {
let outer_variable = 112;
{ // start of code block
let inner_variable = 213;
println!("block variable inner: {}", inner_variable);
println!("block variable outer: {}", outer_variable);
} // end of code block
println!("inner variable: {}", inner_variable); // use of inner_variable outside scope
}
output
rror[E0425]: cannot find value `inner_variable` in this scope
--> main.rs:8:36
|
8 | println!("inner variable: {}", inner_variable); // use of inner_variable outside scope
| ^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `outer_variable`
error: aborting due to previous error
To fix this error, the variable declaration can be moved outside the inner block of code. That way, the scope of the variable spans the entire main() function. This is shown below:
fn main() {
let outer_variable = 112;
let inner_variable = 213;
{ // start of code block
println!("block variable inner: {}", inner_variable);
println!("block variable outer: {}", outer_variable);
} // end of code block
println!("inner variable: {}", inner_variable);
}
output
block variable inner: 213
block variable outer: 112
inner variable: 213
Shadowing
Variable shadowing is a technique in which a variable declared within a certain scope has the same name as a variable declared in an outer scope. This is also known as masking. This outer variable is said to be shadowed by the inner variable, while the inner variable is said to mask the outer variable.
The following code explains the concept.
fn main() {
let outer_variable = 112;
{ // start of code block
let inner_variable = 213;
println!("block variable: {}", inner_variable);
let outer_variable = 117;
println!("block variable outer: {}", outer_variable);
} // end of code block
println!("outer variable: {}", outer_variable);
}
output
block variable: 213
block variable outer: 117
outer variable: 112
What Are Data Types?
- Data Type
Rust is a statically typed language, meaning, it must know the type of all variables at compile time.
How to Define a Type in Rust?
We can define a variable in rust in two different ways:
- Implicit Definition
Unlike other languages like C++ and Java, Rust can infer the type from the type of value assigned to a variable.
The general syntax is :
let variable name = value ;
Explicit Definition
Explicitly tells the compiler about the type of variable.
The general syntax is:
let variable name : data type = value ;
Primitive Types
Rust has a couple of types that are considered primitive. That means they are built-in into the language. There are different data types used for different purposes.
The following illustration shows the different primitive data types in Rust:
**
Scalar Type**
They store a single value.
- Below is the list of scalar types:
- Integers
- Floats
- Boolean
- Character
Compound Type
They group multiple values in one variable. Below is the list of compound types:-
- Array
- Tuple
Numeric Types - Integers and Floats
Integers
Variables of Integer data type hold whole number values. There are two subtypes of integer data type in Rust, based on the number of bits occupied by a variable in memory.
Fixed Size Types
The fixed integer types have a specific number of bits in their notation. This notation is a combination of a letter and a number. The former denotes the category of the integer, whether it is, unsigned or signed, and the latter denotes the size of an integer, i.e., 8, 16, 32, 64.
Below is the list of fixed-length integer types:
i8: The 8-bit signed integer type.
i16: The 16-bit signed integer type.
i32: The 32-bit signed integer type.
i64: The 64-bit signed integer type.
u8: The 8-bit unsigned integer type.
u16: The 16-bit unsigned integer type.
u32: The 32-bit unsigned integer type.
u64: The 64-bit unsigned integer type.
Variable Size Types
The integer type in which the particular size depends on the underlying machine architecture.
💡 Why are there so many types of integers and how do you pick a data type?
The choice depends on what values a variable is expected to hold. So, a programmer should pick a data type that is not so small that the data is lost. Nor should they pick a data type that is so big that it wastes memory.
isize: The pointer-sized signed integer type. usize: The pointer-sized unsigned integer type
Example
The code below defines an integer type both explicitly and implicitly:
- Explicit Definition
The following code explicitly defines the integer variables using the integer type fixed or variable):
fn main() {
//explicitly define an integer
let a:i32 = 24;
let b:u64 = 23;
let c:usize = 26;
let d:isize = 29;
//print the values
println!("a: {}", a);
println!("b: {}", b);
println!("c: {}", c);
println!("d: {}", d);
}
output
a: 24
b: 23
c: 26
d: 29
Implicit Definition
The following code implicitly defines the integer type of the variable by assigning an integer value to the variable.
fn main() {
//explicitly define an integer
let a = 21;
let b = 1;
let c = 54;
let d = 343434;
//print the variable
println!("a: {}", a);
println!("b: {}", b);
println!("c: {}", c);
println!("d: {}", d);
}
output
a: 21
b: 1
c: 54
d: 343434
Floating Point
Floating-point numbers refer to numbers with a fractional part.
The representation of floating-point numbers in a computer’s memory is such that the precision with which a number is stored in memory depends on the number of bits used for storing the variable.
In this respect, there are two subtypes: single-precision f32 and double-precision f64 floating-point, with the latter having more bits to store the number.
f32: The 32-bit floating point type.
f64: The 64-bit floating point type.
Example
The code below defines a floating-point number both explicitly and implicitly:
- Explicit Definition
The following code explicitly defines the float variable using the float type (f32 or f64)
fn main() {
//explicitly define a float type
let f1:f32 = 32.9;
let f2:f64 = 6789.89;
println!("f1: {}", f1);
println!("f2: {}", f2);
}
output
f1: 32.9
f2: 6789.89
Implicit Definition:
The following code implicitly defines the float type of the variable by assigning a floating-point value to the variable:
fn main() {
//implicitly define a float type
let pi = 3.14;
let e = 2.17828;
println!("pi: {}", pi);
println!("e: {}", e);
}
output
pi: 3.14
e: 2.17828
Boolean
The boolean variable can take a value of either true or false. The following code explains how to define a boolean variable in three different ways:
Explicit Definition The following code explicitly defines the variable using the bool keyword:
n main() { //explicitly define a bool let is_bool:bool = true; println!("explicitly_defined: {}", is_bool); }
output
rust explicitly_defined: true
Implicit Definition
The following code implicitly defines the boolean type of a variable by assigning the value true or false to the variable.
fn main() { // assign a boolean value let a = true; let b = false; println!("a: {}", a); println!("b: {}", b); }
Result of an Expression
The result of an expression that evaluates to either true or false (for example a comparison of two values) can be assigned to an implicit boolean variable
fn main() {
// get a value from an expression
let c = 10 > 2;
println!("c: {}", c);
}
Character and String
- Character
The variable is used to store a single character value, such as a single digit or a single alphabet. The value assigned to a char variable is enclosed in a single quote('').
Note: Unlike some other languages, a character in Rust takes up 4 bytes rather than a single byte. It does so because it can store a lot more than just an ASCII value like emojis, Korean, Chinese, and Japanese characters.
Example
The code below defines a character both explicitly and implicitly:
- Explicit Definition
The following code explicitly defines the variable using the char keyword:
fn main() {
// explicitly define
let char_1:char = 'e';
println!("character1: {}", char_1);
}
Output
character1: e
Implicit Definition
The following code implicitly defines the character type of the variable by assigning the single value enclosed within single quotes to them.
fn main() {
// implicitly define
let char_2 = 'a';
let char_3 = 'b';
println!("character2: {}", char_2);
println!("character3: {}", char_3);
}
Output
character2: a
character3: b
String
A string is any sequence of characters enclosed within double quotes (" ").
Example
The code below defines a string both explicitly and implicitly:
- Explicit Definition
The following code explicitly defines the variable using the &str
keyword:
fn main() {
// explicitly define
let str_1:&str = "Rust Programming";
println!("String 1: {}", str_1);
}
Output
String 1: Rust Programming
Implicit Definition
The following code implicitly defines the string type of the variable by assigning the single value enclosed within double quotes to them.
fn main() {
// implicitly define
let str_2 = "Rust Programming";
println!("String 2: {}", str_2);
}
output:-
String 2: Rust Programming
Arrays
- What Is an Array?
An array is a homogenous sequence of elements. Being a compound type, it is used when the collection of values of the same type is to be stored in a single variable. In Rust, an array can only be of a fixed length. Like all other languages, each element in the array is assigned an index. By default, the first element is always at index 0.
Note: By default, arrays are immutable.
Define an Array
To define an array in Rust, we have to define the type and size of the array. To initialize an array, the array elements are enclosed in square brackets []
. The following illustration explains the concept:
#[allow(unused_variables, unused_mut)]
fn main() {
//define an array of size 4
let arr:[i32;4] = [1, 2, 3, 4];
// initialize an array of size 4 with 0
let arr1 = [0 ; 4];
}
The array arr declaration on line 4 declares an array with elements 1,2,3,4.
The array arr1 declaration on line 6 implicitly determines the data type (integer) from the value 0 and 4, is the size of the array. So, this becomes an array consisting of 4 zeros.
Access an Element of an Array
- Any value of the array can be accessed by writing the array name followed by the index number enclosed within square brackets [ ]
.
fn main() {
//define an array of size 4
let arr:[i32;4] = [1, 2, 3, 4];
//print the first element of array
println!("The first value of array is {}", arr[0]);
// initialize an array of size 4 with 0
let arr1 = [0; 4];
//print the first element of array
println!("The first value of array is {}", arr1[0]);
}
output
The first value of array is 1
The first value of array is 0
How to Make an Array Mutable? Just like a variable becomes mutable by adding the mut keyword after let, the same goes for an array.
fn main() {
//define a mutable array of size 4
let mut arr:[i32;4] = [1, 2, 3, 4];
println!("The value of array at index 1: {}", arr[1]);
arr[1] = 9;
println!("The value of array at index 1: {}", arr[1]);
}
output
The value of array at index 1: 2
The value of array at index 1: 9
Print the Array The whole array can be traversed using a loop or the debug trait.
fn main() {
//define an array of size 4
let arr:[i32;4] = [1, 2, 3, 4];
//Using debug trait
println!("\nPrint using a debug trait");
println!("Array: {:?}", arr);
}
output
Print using a debug trait
Array: [1, 2, 3, 4]
Get the Length of the Array To access the length of the array, use the built-in function len.
fn main() {
//define an array of size 4
let arr:[i32;4] = [1, 2, 3, 4];
// print the length of array
println!("Length of array: {}", arr.len());
}
Get Slice
Slice is basically a portion of an array. It lets you refer to a subset of a contiguous memory location. But unlike an array, the size of the slice is not known at compile time.
Syntax
A slice is a two-word object, the first word is a data pointer and the second word is a slice length.
Data pointer is a programming language object that points to the memory location of the data, i.e., it stores the memory address of the data.
To declare an array slice, we need to specify the name of the source array and the range of elements to be included in the slice. Note: If the range of elements is not specified, it will consider the whole array as a slice.
fn main() {
//define an array of size 4
let arr:[i32;4] = [1, 2, 3, 4];
//define the slice
let slice_array1:&[i32] = &arr;
let slice_array2:&[i32] = &arr[0..2];
// print the slice of an array
println!("Slice of an array: {:?}", slice_array1);
println!("Slice of an array: {:?}", slice_array2);
}
output
Slice of an array: [1, 2, 3, 4]
Slice of an array: [1, 2]
What are Tuples?
- Tuples are heterogeneous sequences of elements, meaning, each element in a tuple can have a different data type. Just like arrays, tuples are of a fixed length.
- Define a Tuple
A tuple can be defined by writing let followed by the name of the tuple and then enclosing the values within the parenthesis.
Syntax 1
- The syntax below defines a tuple without specifying the type. However, the compiler can infer the type.
Syntax2
The syntax below defines a tuple by specifying the type.
Example
The following illustration explains the concept:
#[allow(unused_variables, unused_mut)]
fn main() {
//define a tuple
let person_data = ("Alex", 48, "35kg", "6ft");
// define a tuple with type annotated
let person_data : (&str, i32, &str, &str) = ("Alex", 48, "35kg", "6ft");
}
Access the Value of the Tuple
Unlike array which uses
[]
for accessing an element, the value of the tuple can be accessed using the dot operator(.)
.tuplename.indexvalue
To get the individual values out of a tuple, we can use pattern matching to destructure a tuple value, like this:
let person_data = ("Alex", 48, "35kg", "6ft"); let (w, x, y, z) = person_data;
fn main() { //define a tuple let person_data = ("Alex", 48, "35kg", "6ft"); // access value of a tuple println!("The value of the tuple at index 0 and index 1 are {} {}",person_data.0,person_data.1); //define a tuple let person_data = ("Alex", 48, "35kg", "6ft"); // get individual values out of tuple let (w ,x, y, z) = person_data; //print values println!("Name : {}",w); println!("Age : {}",x); println!("Weight : {}",y); println!("Height : {}",z); }
output
The value of the tuple at index 0 and index 1 are Alex 48 Name : Alex Age : 48 Weight : 35kg Height : 6ft
How to Make a Tuple Mutable?
Just like a variable becomes mutable by adding the mut keyword after let, the same goes for a tuple.
fn main() {
//define a tuple
let mut person_data = ("Alex", 48, "35kg", "6ft");
//print the value of tuple
println!("The value of the tuple at index 0 and index 1 are {} {}", person_data.0, person_data.1);
//modify the value at index 0
person_data.0 = "John";
//print the modified value
println!("The value of the tuple at index 0 and index 1 are {} {}", person_data.0, person_data.1);
}
output:-
The value of the tuple at index 0 and index 1 are Alex 48
The value of the tuple at index 0 and index 1 are John 48
Print the Tuple
The whole tuple can be traversed using the debug trait.
fn main() {
//define a tuple
let person_data = ("Alex", 48, "35kg", "6ft");
//print the value of tuple
println!("Tuple - Person Data : {:?}",person_data);
}
output:-
Tuple - Person Data : ("Alex", 48, "35kg", "6ft")
What Are Constant Variables?
- Constant variables are declared constant throughout the program scope, meaning, their value cannot be modified. They can be defined in the global and local scopes.
- They are declared using the
const
keyword followed by the name of the variable, colon(:)
, and then the data type of the variable.
Naming Convention: By convention, you write a constant variable name in a SCREAMING_SNAKE_CASE, i.e.,
All letters should be UPPER case.
All words should be separated using an underscore
( _ )
The following example defines two const variables:
ID_1 in global scope
ID_2 in local scope
```rust
const ID_1: i32 = 4; // define a global constant variable
fn main() {
const ID_2: u32 = 3; // define a local constant variable
println!("ID:{}", ID_1); // print the global constant variable
println!("ID:{}", ID_2); // print the local constant variable
}
```
output:
```rust
ID:4
ID:3
```
Difference Between const and let Variables
There are many differences between const and let variables.
- Declaration
- Constant variables are declared using the const keyword unlike let variables.
- Scope
- const variables are declared in global and local scope unlike let variables that are declared only in the local scope.
- Mutability
- const variable cannot be mutable unlike let which can be made mutable using mut keyword.
- Data Type *Unlike let variables, it is mandatory to define the data type of const variables.
- Set Value at Run-time
- The value of const variable can only be set before running the program whereas the let variable can store the result at runtime.
- Shadowing
- Unlike let variables, const variables cannot be shadowed.
Introduction to Operators
Operators
- An operator is a symbol that takes one or more values and outputs another. It tells the compiler to perform some sort of operation.
Types of Operators
- Different operators are available in Rust for performing different operations. Based on the number of operands, the operators can be categorized into binary and unary operators:
Unary Operators
- The operators that act upon a single operand are unary operators.
Types
Borrow Expression
Dereference Expression
Negation Expression
Logical Negation Expression
Binary Operators
* The operators that deal with two operands are binary operators.
Arithmetic Operators
What Are Arithmetic Operators?
- Arithmetic operators are used to perform arithmetic operations.
Types The table below summarizes the arithmetic operations in Rust.
Types
The table below summarizes the arithmetic operations in Rust.
operator | operation | explanation |
operand1 + operand2 | addition | add operand1 and operand2 |
operand1 - operand2 | subtraction | subtract operand2 from operand1 |
operand1 / operand2 | divide | divide operand1 by operand2 |
operand1 * operand2 | multiplication | multiply operand1 with operand2 |
operand1 % operand2 | modulus | get reminder of operand1 by dividing with operand2 |
The following example shows the use of arithmetic operators in a program:
fn main() {
let a = 4;
let b = 3;
println!("Operand 1:{}, Operand 2:{}", a , b);
println!("Addition:{}", a + b);
println!("Subtraction:{}", a - b);
println!("Multiplication:{}", a * b);
println!("Division:{}", a / b);
println!("Modulus:{}", a % b);
}
output
Operand 1:4, Operand 2:3
Addition:7
Subtraction:1
Multiplication:12
Division:1
Modulus:1
Logical Operators
What Are Logical Operators?
Logical operators operate on true / false values
Types
The following table summarizes the types and functions of the logical operators:
operator | operation | explanation |
operand1 && operand2 | AND | Evaluates to true if operand 1 and Operand 2 both evaluates to be true |
operand1 | operand2 | |
! operand1 | NOT | negates the value of single operand |
The logical AND and OR are known as Lazy Boolean expressions because the left-hand side operand of the operator is first evaluated. If it is false, there is no need to evaluate the right-hand side operand in case of AND. If it is true, there is no need to evaluate the right-hand side operand in case of OR.
The following example shows the use of logical operators in a program:
fn main() {
let a = true;
let b = false;
println!("Operand 1:{}, Operand 2:{}", a , b);
println!("AND:{}", a && b);
println!("OR:{}", a || b);
println!("NOT:{}", ! a);
}
output
Operand 1:true, Operand 2:false
AND:false
OR:true
NOT:false
Comparison Operators
What Are Comparison Operators?
Comparison Operators are used for comparing the values of two operands.
Types
Below is the list of comparison operators in Rust.
operator | operation | explanation |
operand1 > operand2 | greater then | Evaluates to true if operand 1 is greater then Operand 2 |
operand1 < operand2 | lesser then | Evaluates to true if operand 1 is lesser then Operand 2 |
operand1 <= operand2 | less then equal to | Evaluates to true if operand 1 is lesser or equal to the Operand 2 |
operand1 >= operand2 | greater then equal to | Evaluates to true if operand 1 is greater or equal to the Operand 2 |
operand1 == operand2 | equal to | Evaluates to true if operand 1 equal to the Operand 2 |
operand1 != operand2 | Not equal to | Evaluates to true if operand 1 not equal to the Operand 2 |
The following example shows the use of comparison operators in a program:
fn main() {
let a = 2;
let b = 3;
println!("Operand 1:{}, Operand 2:{}", a , b);
println!("a > b:{}", a > b);
println!("a < b:{}", a < b);
println!("a >= b:{}", a >= b);
println!("a <= b:{}", a <= b);
println!("a == b:{}", a == b);
println!("a != b:{}", a != b);
}
output
Operand 1:2, Operand 2:3
a > b:false
a < b:true
a >= b:false
a <= b:true
a == b:false
a != b:true
Bitwise Operators
- What Are Bitwise Operators? Bitwise operators deal with the binary representation of the operands.
Types
The table below summarizes the types of bitwise operators in Rust.
operator | operation | explanation |
operand1 & operand2 | AND | bitwise AND operand1 and operand2 |
operand1 | operand2 | OR |
operand1 ^ operand2 | XOR | bitwise XOR operand1 and operand2 |
! operand1 | NOT | Inverse the bit of operand |
<< operand | Left shift | moves all the operand1 to the left by the number of places specified in the operand 2 |
new bits filled with zeros . shifting a value left by one position is equivalent to multiplying it by 2 , | ||
Shifting to positions is equivalent to multiplying it by 4 and so on | ||
\>> operand | Right Shift | moves all the operand1 to the right by the number of places specified in the operand 2 |
new bits filled with zeros . shifting a value right by one position is equivalent to multiplying it by 2 , | ||
Shifting to positions is equivalent to multiplying it by 4 and so on |
📝 Note: Right shift >> is same as arithmetic right shift on signed integer types, logical right shift on unsigned integer types.
- Example
The example below shows the bitwise AND, OR, XOR, Left Shift, and Right Shift operations.
The following example shows the use of bitwise operators in a program:
fn main() {
let a = 5;
let b = 6;
println!("Operand 1: {}, Operand 2: {}", a , b);
println!("AND: {}", a & b);
println!("OR: {}", a | b);
println!("XOR: {}", a ^ b);
println!("NOT a: {}", !a);
println!("Left shift: {}", a << 2);
println!("Right shift: {}", a >> 1);
}
output
Operand 1: 5, Operand 2: 6
AND: 4
OR: 7
XOR: 3
NOT a: -6
Left shift: 20
Right shift: 2
Assignment and Compound Assignment Operators
- Assignment Operator The assignment operator is used to save a value in the variable.
Type
Rust has only one assignment operator, = . The following table defines the function of the operator.
operator | operation | explanation | example |
operand1 = operand2 | assign a value | assign a value of operand 2 to operand 1 | a = 1 |
b = a |
The following example demonstrates the use of some of the assignment operator in a program:
fn main() {
let a = 2;
let b = a;
println!("b = a");
println!("Value of a:{}", a);
println!("Value of b:{}", b);
}
output:-
b = a
Value of a:2
Value of b:2
Compound Assignment Operator
The compound assignment operator is used to perform an operation and then assign that value to the operand.
Types
The following table summarizes the types of compound assignment operators
operator | operation | explanation |
operand1 += operand2 |
operand1 -= operand2 | add a value and assign
subtract a value and assign | add left-hand side to right-hand side and then save updated value to left operand
add right-hand side to right-hand side and then save updated value to left operand | | operand1 /= operand2
operand1 *= operand2 | divide a value and assign
multiple a value and assign | divide left-hand side to right-hand side and then save updated value to left operand
multiply left-hand side to right-hand side and then save updated value to left operand | | operand1 %= operand2 | modulus and assign | take modulus of the left-hand side with right-hand operand and then save updated value to left operand | | operand1 &= operand2 | Bitwise AND and assign | Bitwise AND of the left-hand side with right-hand operand and then save updated value to left operand | | operand1 |= operand2 | Bitwise OR and assign | Bitwise OR of the left-hand side with right-hand operand and then save updated value to left operand | | operand1 ^= operand2 | Bitwise XOR and assign | Bitwise XOR of the left-hand side with right-hand operand and then save updated value to left operand | | <<= operand1 | left sift and assign | left shift the operand x times then save updated value to operand | | >>= operand1 | right shift and assign | right shift the operand x times then save updated value to operand |
The following example demonstrates the use of some of these operators in a program:
fn main() {
//define a mutable variable
let mut a = 2;
println!("a:{}", a);
a += 1;
println!("a+=1:{}", a);
println!("a:{}", a);
a -= 1;
println!("a-=1:{}", a);
println!("a:{}", a);
a /= 1;
println!("a/=1:{}", a);
println!("a:{}", a);
a *= 3;
println!("a/=3:{}", a);
}
output:
a:2
a+=1:3
a:3
a-=1:2
a:2
a/=1:2
a:2
a/=3:6
Type Casting Operator
What Is Type Casting? Type casting is when you convert the data type of the variable to some other data type.
- Type Casting in Rust In Rust, typecasting is done using the as keyword followed by the desired data type of the variable or value.
The following example demonstrates the use of type casting operator in a program:
fn main() {
let a = 15;
let b = (a as f64) / 2.0;
println!("a: {}", a);
println!("b: {}", b);
}
output
a: 15
b: 7.5
📝 What data types can be type casted?
Integer can be type casted to floating-point and vice versa.
Integer can be typecasted to String
📝What data types cannot be type casted?
String (&str) or character cannot be type casted to the data type of type integer or float.
Character cannot be type casted to String type and vice versa
The following code gives an error, ❌, because of the invalid type casting operation:
fn main() {
let a: char = 'r' ; // cannot be type casted
let b = a as &str ;
println!("a: {}", a);
println!("b: {}", b);
}
Borrowing and Dereferencing Operators
Borrowing Operator
Borrowing means to reference the original data binding or to share the data.References are just like pointers in C.
Two variables are involved in a borrowing relationship when the referenced variable holds a value that the referencing variable borrows. The referencing variable simply points to the memory location of the referenced variable.
The following illustration shows that operand 1 borrows the value of operand 2 using two types of operators:
Types
Borrowing can be of two types:
Shared borrowing
A piece of data that is shared by single or multiple variables but it cannot be altered
Mutable borrowing
A piece of data that is shared and altered by a single variable (but the data is inaccessible to other variables at that time)
The following table summarizes the function of these two types.
operator | operation | explanation |
Operand1 = & Operand2 | shared borrow | operand 1 can read data of another operand 2 |
Operand1 = & mut Operand2 | mutable borrow | Operand 1 can read and alter data of another operand2 |
Example
The following example shows a shared borrow and mutable borrow:
fn main() {
let x = 10;
let mut y = 13;
//immutable reference to a variable
let a = &x;
println!("Value of a:{}", a);
println!("Value of x:{}", x); // x value remains the same since it is immutably borrowed
//mutable reference to a variable
let b = &mut y;
println!("Value of b:{}", b);
println!("Value of y:{}", y); // y value is changed since it is mutably borrowed
}
output:-
Value of a:10
Value of x:10
Value of b:13
Value of y:13
Dereferencing Operator
Once you have a mutable reference to a variable, dereferencing is the term used to refer to changing the value of the referenced variable using its address stored in the referring variable.
The following illustration shows that operand 1 mutably borrows the value of operand 2 using & mut and then operand 1 dereferences the value of operand 2 using the *
operator:
Type
The following table shows the dereferencing operator *
along with its function .
operator | operation | explanation |
*Operand1 = Operand2 | Dereferencing a value | point to the value of a mutable borrow variable and can also |
update that variable value |
Example
The following example shows how to dereference a variable:
fn main() {
//mutable reference to a variable
let mut x = 10;
println!("Value of x:{}", x);
let a = & mut x;
println!("Value of a:{}", a);
//dereference a variable
*a = 11;
println!("Value of a:{}", a);
println!("Value of x:{}", x); // Note that value of x is updated
}
output:
Value of x:10
Value of a:10
Value of a:11
Value of x:11
Precedence and Associativity
Precedence
The precedence of an operator determines which operation is performed first in an expression with more than one operators.
Operators are listed below in the order of their precedence from highest to lowest :
Unary
Logical/Bitwise NOT -
!
Dereference -
*
Borrow -
&
,&mut
Binary
Typecast -
as
Multiplication-
*
, Division -/
, Remainder-%
Addition -
+
, Subtraction --
Left Shift -
<<
, Right Shift ->>
Bitwise AND -
&
Bitwise XOR -
^
Bitwise OR -
|
Comparison -
==
!=
<`` >
<= ``>=
Logical AND -
&&
Logical OR -
||
Range -
start .. stop
Assignment/Compound Assignment -
= += -= *= /= %= &= |= ^= <<= >>=
Note: The operators that are written in the same row have the same order of precedence.
Associativity
If two or more operators of the same precedence appear in a statement, then which operator will be evaluated first is defined by the associativity.
Left to Right Associativity
Left associativity occurs when an expression is evaluated from left to right. An expression such as a ~ b ~ c
, in this case, would be interpreted as (a ~ b) ~ c
where ~
can be any operator. The operators below can be chained as left associative.
📝The comparison, assignment, and the range operator cannot be chained at all.
Example 1
The example below solves an expression according to its operator precedence:
fn main() {
println!("Answer : {}",( 3 + 5 ) * 9 / 7 & 8);
}
output:-
Answer : 8
Example 2
The example below solves an expression according to its operator precedence:
fn test() {
println!("{}", 2 + 3 / 5 ^ 7 & 8 | 9);
}
output:
11
If Expression
There can be multiple conditional constructs using an if statement.
If expression
If…else expression
If…else if…else expression
Nested if expression
Shorthand if expression
Let’s discuss each one of them in detail:-
If Expression
If expression takes a condition. If the condition within the if expression evaluates to be true, then the block of code is executed.
Syntax The general syntax is:
llustration
The following flow chart explains the concept of an if statement:
fn main() { //define a variable let learn_language = "Rust"; // if construct if learn_language == "Rust" { println!("You are learning Rust language!"); } }
output
Your are learning Rust langauage!
If…else Expression
In an
if..else
construct, if the condition within the if expression evaluates to be false, then the statement within the else block is executed.- Syntax
The general syntax is:

#### llustration
The following flow chart explains the concept of an if..else if..else expression:

fn main() {
//define a variable
let learn_language = "Rust";
// if else construct
if learn_language == "Rust" {
println!("You are learning Rust language!");
}
else {
println!("You are learning some other language!");
}
}
Output
You are learning Rust language!
if…else if…else Expression
If there are multiple conditions to be checked, then if..else if..else construct is used.
Syntax The general syntax is:
llustration
The following flow chart explains the concept of an if..else if..else expression:
fn main() {
//define a variable
let learn_language="Rust";
// if..elseif..else construct
if learn_language == "Rust" {
println!("You are learning Rust language!");
}
else if learn_language == "Java" {
println!("You are learning Java language!");
}
else {
println!("You are learning some other language!");
}
}
Output
You are learning Rust language!
Nested if Expression
An if expression inside the body of another if expression is referred to as a nested if expression.
- Syntax An if construct is enclosed within an if construct. The general syntax is:
Note: The nested if expression can also be written with a AND expression in an if.
if condition1 && condition2
{
//statement
}
This is true only if the second if statement is the only thing inside the first if.
illustration
The following flow chart explains the concept of a nested if statement .
Note: There can be as many levels of nesting as you want.
fn main() {
//define a variable
let learn_language1 = "Rust";
let learn_language2 = "Java";
// outer if statement
if learn_language1 == "Rust" { // inner if statement
if learn_language2 == "Java"{
println!("You are learning Rust and Java language!");
}
}
else {
println!("You are learning some other language!");
}
}
output:-
You are learning Rust and Java languages
Shorthand if
Instead of writing a lengthy if-else construct, we can use a shorthand if.
Syntax The general syntax is:
Note: This is similar to a ternary operator in languages like C and C++.
fn main() {
//define a variable
let learn_language = "Rust";
// short hand construct
let res= if learn_language == "Rust" {"You are learning Rust language!"} else {"You are learning some other language!"};
println!("{}", res);
}
Note: Expressions can return a value, unlike statements. Recall that the semicolon turns any expression into a statement. It throws away its value and returns a unit () instead.
fn main() {
let x = "Rust";
let y: bool = if x == "Rust" { true } else { false };
// let z: bool = if x == "Rust" { true; } else { false; };
println!("x:{}", x);
println!("y:{}", y);
Note: Uncommenting line 6 in the above code gives an error ❌ since we are trying to convert an expression to a statement and hence not returning a value.
If Let Expression
What Is an if let Expression?
if let is a conditional expression that allows pattern matching. The block of code in the construct executes if the pattern in the condition matches with that of scrutinee expression.
- Syntax The if let expression begins with an if followed by a let and then a pattern having values enclosed within round brackets. Then an equal to
(=)
followed by a scrutinee expression. Then there is a block of code enclosed withinbraces{}
.Then there is also an optional else block after this.
Note: When it says matching of pattern, it means that the defined pattern has the same number of values as that of the scrutinee expression.
Examples
The following examples show how the different cases of if let expression can work:
fn main() {
// define a scrutinee expression
let course = ("Rustlabs", "beginner","course");
// pattern matches with the scrutinee expression
if let ("Rustlabs", "beginner","course") = course {
println!("Wrote all values in pattern to be matched with the scrutinee expression");
} else {
// do not execute this block
println!("Value unmatched");
}
}
output:-
Wrote all values in pattern to be matched with the scrutinee expression
If the first value or second value matches, it can guess the third value.
fn main() {
// define a scrutinee expression
let course = ("Rustlab", "beginner","course");
// pattern matches with the scrutinee expression
if let ("Rustlab", "beginner", c) = course {
println!("Wrote first two values in pattern to be matched with the scrutinee expression : {}", c);
}
else {
// do not execute this block
println!("Value unmatched");
}
}
output :
Wrote first two values in pattern to be matched with the scrutinee expression : course
If the first value matches, it can guess the other two values.
fn main() {
// define a scrutinee expression
let course = ("Rustlabs", "beginner","course");
// pattern matches with the scrutinee expression
if let ("Rustlabs", c, d) = course {
println!("Wrote one value in pattern to be matched with the scrutinee expression.Guessed values: {}, {}",c,d);
} else {
// do not execute this block
println!("Value unmatched");
}
}
output:-
Wrote one value in pattern to be matched with the scrutinee expression.Guessed values: beginner, course
Case 2: When the Pattern is Not Matched
In the example below, the defined pattern does not match with the scrutinee expression so the statement in the else block gets executed.
fn main() {
// define a scrutinee expression
let course = ("Rust", "beginner");
// pattern does not match with the scrutinee expression
if let ("Java", c) = course {
println!("Course is {}", c);
} else {
// execute this block
println!("Value unmatched");
}
}
output:-
Value unmatched
Case 3: When the Pattern is Replaced With _
In the example below, the pattern is not defined. Rather it is replaced with an _
. In this case, the statement within the if let block executes.
Note: A warning, ⚠️, is generated by the compiler because the Rust compiler complains that it doesn’t make sense to use if let
with an irrefutable pattern.
fn main() {
// no pattern is define
if let _ = 10 {
println!("irrefutable if-let pattern is always executed");
}
}
output:-
irrefutable if-let pattern is always executed
warning: irrefutable if-let pattern
--> main.rs:3:5
|
3 | / if let _ = 10 {
4 | | println!("irrefutable if-let pattern is always executed");
5 | | }
| |_____^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
Match Expression
What Is a match Expression?
Match expression checks if the current value corresponds to any value within the list of values. Match expressions are similar to switch statements in languages like C and C++. They give a more compact code when compared with the if/else construct.
Syntax Match expression uses a matching keyword. The match expression can be written in two different ways, which are given below:
Method 1: If you do not want to assign a value to the result variable from within the match block
fn main() { // define a variable let x = 5; // define match expression match x { 1 => println!("Java"), 2 => println!("Python"), 3 => println!("C++"), 4 => println!("C#"), 5 => println!("Rust"), 6 => println!("Kotlin"), _ => println!("Some other value"), }; }
output:-
Rust
Method 2:
If you want to assign a value to the result variable from within the match block
fn main(){
// define a variable
let course = "Rust";
// return value of match expression in a variable
let found_course = match course {
"Rust" => "Rust",
"Java" => "Java",
"C++" => "C Plus Plus",
"C#" => "C Sharp",
_ => "Unknown Language"
};
println!("Course name : {}",found_course);
}
output:-
Course name : Rust
Comparison of The Different Conditional Constructs
Use if
Statement
Use an if statement if:
It is desired to test the truthiness of an expression
There is a single affirmative test
There is a need to evaluate different expressions for each branch
It can test expressions based on ranges of values or conditions
Use match
Statement
Use a
match
statement if:It is desired to compare multiple possible conditions of an expression
The values belong to the same domain
It can test expressions based on values only, i.e., condition cannot take ranges
Use if let
Statement
Use an if let statement if:
- There is a pattern in the condition and it is to be matched with the scrutinee expression
What Is a Loop?
Loops are used to iterate until a defined condition is reached.
Types of Loops
There are two types of loops in Rust:
Definite Loops :- Loops in which the number of iterations is known at compile time.
Example
- for
Indefinite Loops :- Loops in which the number of iterations is not known at compile time.
Example
while
loop
Definite Loop - For Loop
What Is a for Loop?
A for loop is a definite loop, meaning, the number of iterations is defined.
Syntax
The for loop is followed by a variable that iterates over a list of values. The general syntax is :
Example
The following example uses a for loop that prints 5 numbers.
fn main() {
//define a for loop
for i in 0..5 {
println!("{}", i);
}
}
output
0
1
2
3
4
An explanation
for loop definition
On line 3 a for loop is defined.
- Variable i is an iterator variable that iterates over the range with the lower bound as 0 and the upper bound as 5. From here the body of the loop starts.
for loop body
The body of the for loop is defined from line 3 to line 5
In each iteration:
On line 4, the value of the variable i is printed.
The value of the variable i is incremented by 1.
The iterator variable i traverses over the range until the upper bound is reached.
Note: The lower bound is inclusive and the upper bound is exclusive in the range
The following illustration explains this concept:
Enumerate
To count how many times the loop has already executed, use the .enumerate() function.
- Syntax The general syntax is :
- Example
The example below prints the frequency of iterations and the value of variable.
fn main() {
for (count, variable) in (7..10).enumerate() {
println!("count = {}, variable = {}", count, variable);
}
}
output
count = 0, variable = 7
count = 1, variable = 8
count = 2, variable = 9
Explanation
enumerated for loop definition
On line 2 an enumerate for loop is defined.
The variable variable iterates over the range with the lower bound as 7 and the upper bound as 10 and a variable count which shows how many times the loop is iterated. From here the body of the loop starts.
enumerated for loop body
- On line 3, the value of count and variable is printed and then incremented by 1.
indefinite Loop - While and Loop
- While loop
While loop also iterates until a specific condition is reached. However, in the case of a while loop, the number of iterations is not known in advance.
- Syntax The while keyword is followed by a condition that must be true for the loop to continue iterating and then begins the body of the loop. the general syntax is :
- Example
The following example makes use of a while loop to print a variable value. The loop terminates when the variable’s value modulus 3
equals 1
fn main() {
let mut var = 1; //define an integer variable
let mut found = false; // define a boolean variable
// define a while loop
while !found {
var=var+1;
//print the variable
println!("{}", var);
// if the modulus of variable is 1 then found is equal to true
if var % 3 == 1 {
found = true;
}
println!("Loop runs");
}
}
output
2
Loop runs
3
Loop runs
4
Loop runs
Explanation
A mutable variable, var, is defined on line 2.
A mutable variable, found, is defined on line 3.
while loop definition
while loop is defined on line 5. while loop is followed by a variable found. found is initially set to false. !found means that the loop will continue to iterate until the value of found evaluates to be true.The loop terminates when found is set to true. From here the body of the while loop starts
while loop body
The body of the loop is defined from line 5 to line 14.
In each iteration:
The value of the variable var is incremented by 1 on line 6 and then printed on line 8.
If the value of the var modulus 3 is equal to 1, then the value of found is set to true else it prints “loop runs” on line 13 and the loop continues.
The following illustration traces the execution of the program:
Loop
If you want the iteration to continue infinitely, then use the loop keyword before the block of code.
- Syntax
The loop keyword is followed by the body of the loop enclosed within curly brackets {}
. The general syntax is :
Example
The following example shows how the loop runs infinitely using a loop. Note: The maximum time that is set for the code to run on our platform is 30sec. Since the code below runs more than that, it won’t execute here. However, the code will continue to run indefinitely.
fn main() {
//define an integer variable
let mut var = 1;
// define a while loop
loop {
var = var + 1;
println!("{}", var);
}
}
output:
Loop continues infinite times
Explanation
A mutable variable var is defined on line 3.
loop definition
- loop is defined on line 5.
From here the body of the loop starts
loop body
The body of the loop is defined from line 5 to line 8.
In each iteration, the value of the variable var is incremented by 1 on line 6 and then printed on line 7.
The loop continues to iterate infinitely.
Break Statement
What Is a break
Statement?
The break statement terminates the loop. It is generally placed inside a conditional statement so that the loop terminates if the associated condition is true.
Break statement is valid in case of while, for and loop.
Using With a for Loop
Below is an example of break expression, using a for loop.
The range defined in the for loop is from 0 to 10.
Within the for loop :
The value of
i
is printedWhen the value of
i
is equal to5
, the loop terminates
fn main() {
// define a for loop
for i in 0..10 {
println!("i:{}", i);
if i == 5 {
break;
}
}
}
output
i:0
i:1
i:2
i:3
i:4
i:5
Using With a while Loop
Below is an example of break expression, using a while loop.
A mutable variable i is defined
A boolean variable found is defined Within the while loop body :
The value of i is printed
When the value of
i
is equal to5
, the loop terminates
fn main() {
let mut i = 1;
let found = false;
// define a while loop
while !found {
println!("i:{}", i);
if i == 5 {
break;
}
i = i + 1;
}
}
output
i:1
i:2
i:3
i:4
i:5
Using With a loop
Below is an example of break expression, using a loop.
A mutable variable i is defined
Within the loop body:
The value of i is printed
When the value of
i
is equal to4
, the loop terminates
The infinite loop is turned into a “manageable” loop.
fn main() {
let mut i = 1;
// define a loop
loop{
println!("i:{}", i);
if i == 5 {
break;
}
i = i + 1;
}
}
Output
i:1
i:2
i:3
i:4
i:5
Continue Statement
- What Is a continue Statement?
The continue statement, when encountered inside a loop, skips the execution of the rest of the statements in the loop’s body for the current iteration and returns the control to the start of the loop.
Using With a for Loop
Below is an example of a continue expression, using a for loop.
The range defined in the for loop is from
0
to10
with var variable used for iterating over the loopWithin the for loop:
The value of var is printed
When the value of
var
is equal to4
, the control goes to the start of the loopThe loop executes until the upper bound for the defined range is reached
fn main() {
// define a for loop
for var in 0..10 {
if var == 4 {
println!("I encoutered a continue statement");
continue;
}
println!("var: {}", var);
println!("I did not encounter continue statement");
}
}
output:-
var: 0
I did not encounter continue statement
var: 1
I did not encounter continue statement
var: 2
I did not encounter continue statement
var: 3
I did not encounter continue statement
I encoutered a continue statement
var: 5
I did not encounter continue statement
var: 6
I did not encounter continue statement
var: 7
I did not encounter continue statement
var: 8
I did not encounter continue statement
var: 9
I did not encounter continue statement
Using With a while Loop
Below is an example of continue expression, using a while loop.
A mutable variable var is defined
A boolean variable found is defined
Within the while loop body:
The value of var is printed
When the value of var is equal to
4
, the control goes to the start of the loop.The loop executes until the value of found does not equal true.
fn main() {
// define an integer variable
let mut var = 1;
// define a boolean variable
let mut found = false;
// define a while loop
while !found {
var = var + 1;
println!("{}", var);
if var == 4 {
println!("I encoutered a continue statement");
continue;
}
println!("I did not encounter continue statement");
if var == 10{
found = true;
}
}
}
Output
2
I did not encounter continue statement
3
I did not encounter continue statement
4
I encoutered a continue statement
5
I did not encounter continue statement
6
I did not encounter continue statement
7
I did not encounter continue statement
8
I did not encounter continue statement
9
I did not encounter continue statement
10
I did not encounter continue statement
Using With a loop
Below is an example of continue expression, using a loop .
A mutable variable var is defined
A boolean variable found is defined
Within the loop body:
The value of var is printed
When the value of var is equal to
4
, the control goes to the start of the loopThe loop executes infinitely
Note: This code widget will give an error, ❌, due to limitations of our platform but on the local machine, it will run an infinite loop.
fn main() {
// define an integer variable
let mut var = 1;
// define a loop
loop {
var = var + 1;
println!("{}", var);
if var == 4 {
println!("I encoutered continue statement");
continue;
}
println!("I did not encounter continue statement");
}
}
Nested Loops
What Is a Nested Loop? A nested loop is a loop within a loop.
Syntax Here, a for loop nested inside a for loop. However, any loop can be nested inside any loop. The general syntax is :
Example
The following code prints a multiplication table of 1-5 using a nested for loop.
fn main() {
for i in 1..5{ //outer loop
println!("Multiplication Table of : {}", i);
for j in 1..5 { // inner loop
println!("{} * {} = {}", i, j, i * j);
}
}
}
output
Multiplication Table of : 1
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
1 * 4 = 4
Multiplication Table of : 2
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
Multiplication Table of : 3
3 * 1 = 3
3 * 2 = 6
3 * 3 = 9
3 * 4 = 12
Multiplication Table of : 4
4 * 1 = 4
4 * 2 = 8
4 * 3 = 12
4 * 4 = 16
Explanation
Outer for Loop
A for loop is defined on line 2
The loop takes i as an iterator that iterates over values from 1 to 5.
Inner for Loop
A for loop is defined on line 4
The loop takes j as an iterator that iterates over values from 1 to 5.
For each iteration of the outer loop, the inner loop runs four times while printing the product of variables i and j.
i | j | output |
1 | 1 | |
2 | ||
3 | ||
4 | 1 *1 = 1 | |
1* 2 = 2 | ||
1 *3 = 3 | ||
1* 4 = 4 | ||
2 | 1 | |
2 | ||
3 | ||
4 | 2 *1 = 2 | |
2* 2 = 4 | ||
2 *3 = 6 | ||
2* 4 = 8 | ||
3 | 1 | |
2 | ||
3 | ||
4 | 3 *1 = 3 | |
3* 2 = 6 | ||
3 *3 = 9 | ||
3* 4 = 12 | ||
4 | 1 | |
2 | ||
3 | ||
4 | 4 *1 = 4 | |
4* 2 = 8 | ||
4 *3 = 12 | ||
4* 4 = 16 |
Sometimes, you might need to break the outer loop instead of the inner loop. So how can you specify which loop you are referring to? You can use a loop label.
Loop Labels
What Is a Loop Label? A loop label assigns an identifier to a loop.
Syntax Write a label and colon before the loop.
- Example The code below prints the multiplication table of 1 to 5 except 3. See how specifying a loop label helps you to skip the table of 3.
fn main() {
'outer:for i in 1..5 { //outer loop
println!("Muliplication Table : {}", i);
'inner:for j in 1..5 { // inner loop
if i == 3 { continue 'outer; } // Continues the loop over `i`.
if j == 2 { continue 'inner; } // Continues the loop over `j`.
println!("{} * {} = {}", i, j, i * j);
}
}
}
output:
Muliplication Table : 1
1 * 1 = 1
1 * 3 = 3
1 * 4 = 4
Muliplication Table : 2
2 * 1 = 2
2 * 3 = 6
2 * 4 = 8
Muliplication Table : 3
Muliplication Table : 4
4 * 1 = 4
4 * 3 = 12
4 * 4 = 16
Explanation
Outer for Loop
A for loop is defined on line
2
.The loop has a label outer . It takes i as an iterator that iterates over values from
1
to4
.
Inner for Loop
A for loop is defined on line
3
The loop has a label inner. It takes j as an iterator that iterates over values from
1
to5
.For each i the inner loop iterate j times and prints the product
i * j
.When the outer loop increments
i
to3
and the inner loop starts from j = 1, the conditioni == 3
is found to be true and the continue ‘outer statement causes execution to be transferred to the next iteration of the outer loop on line2
. The variablei
is incremented to4
and the execution continues.When the value of j increments to
2
, then the 2nd iteration of the inner loop gets skipped and continue 'inner causes the execution to be transferred to the next iteration of the inner loop on line4
. The variable j is incremented to3
and the execution continues.
this series will continue with more hand-on labs .