Rust - Quick Guide

Rust is a systems level programming language, developed by Graydon Hoare. Mozilla Labs later acquired the programme.
Application programming languages like Java/C# are used to build software, which provide services to the user directly. They help us build business applications like spreadsheets, word processors, web applications or mobile applications.
Systems programming languages like C/C++ are used to build software and software platforms. They can be used to build operating systems, game engines, compilers, etc. These programming languages require a great degree of hardware interaction.
Systems and application programming languages face two major problems −
It is difficult to write secure code.
It is difficult to write multi-threaded code.
Rust focuses on three goals −
Safety
Speed
Concurrency
The language was designed for developing highly reliable and fast software in a simple way. Rust can be used to write high-level programs down to hardware-specific programs.
Rust programming language does not have a Garbage Collector (GC) by design. This improves the performance at runtime.
Software built using Rust is safe from memory issues like dangling pointers, buffer overruns and memory leaks.
Rust’s ownership and memory safety rules provide concurrency without data races.
Web Assembly helps to execute high computation intensive algorithms in the browser, on embedded devices, or anywhere else. It runs at the speed of native code. Rust can be compiled to Web Assembly for fast, reliable execution.
Installation of Rust is made easy through rustup, a console-based tool for managing Rust versions and associated tools.
Let us learn how to install RUST on Windows.
Installation of Visual Studio 2013 or higher with C++ tools is mandatory to run the Rust program on windows. First, download Visual Studio from here VS 2013 Express
Installation of Visual Studio 2013 or higher with C++ tools is mandatory to run the Rust program on windows. First, download Visual Studio from here VS 2013 Express
Download and install rustup tool for windows. rustup-init.exe is available for download here − Rust Lang
Download and install rustup tool for windows. rustup-init.exe is available for download here − Rust Lang
Double-click rustup-init.exe file. Upon clicking, the following screen will appear.
Double-click rustup-init.exe file. Upon clicking, the following screen will appear.
Press enter for default installation. Once installation is completed, the following screen appears.
Press enter for default installation. Once installation is completed, the following screen appears.
From the installation screen, it is clear that Rust related files are stored in the folder −
C:\Users\{PC}\.cargo\bin

From the installation screen, it is clear that Rust related files are stored in the folder −
C:\Users\{PC}\.cargo\bin
The contents of the folder are −
cargo-fmt.exe
cargo.exe
rls.exe
rust-gdb.exe
rust-lldb.exe
rustc.exe // this is the compiler for rust
rustdoc.exe
rustfmt.exe
rustup.exe

Cargo is the package manager for Rust. To verify if cargo is installed, execute the following command −
Cargo is the package manager for Rust. To verify if cargo is installed, execute the following command −
C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)

The compiler for Rust is rustc. To verify the compiler version, execute the following command −
The compiler for Rust is rustc. To verify the compiler version, execute the following command −
C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)

To install rustup on Linux or macOS, open a terminal and enter the following command.
$ curl https://sh.rustup.rs -sSf | sh

The command downloads a script and starts the installation of the rustup tool, which installs the latest stable version of Rust. You might be prompted for your password. If the installation is successful, the following line will appear −
Rust is installed now. Great!

The installation script automatically adds Rust to your system PATH after your next login. To start using Rust right away instead of restarting your terminal, run the following command in your shell to add Rust to your system PATH manually −
$ source $HOME/.cargo/env

Alternatively, you can add the following line to your ~/.bash_profile −
$ export PATH="$HOME/.cargo/bin:$PATH"

NOTE − When you try to compile a Rust program and get errors indicating that a linker could not execute, that means a linker is not installed on your system and you will need to install one manually.
A Read-Evaluate-Print Loop (REPL) is an easy to use interactive shell to compile and execute computer programs. If you want to compile and execute Rust programs online within the browser, use Tutorialspoint Coding Ground.
This chapter explains the basic syntax of Rust language through a HelloWorld example.
Create a HelloWorld-App folder and navigate to that folder on terminal
Create a HelloWorld-App folder and navigate to that folder on terminal
C:\Users\Admin>mkdir HelloWorld-App
C:\Users\Admin>cd HelloWorld-App
C:\Users\Admin\HelloWorld-App>

To create a Rust file, execute the following command −
To create a Rust file, execute the following command −
C:\Users\Admin\HelloWorld-App>notepad Hello.rs

Rust program files have an extension .rs. The above command creates an empty file Hello.rs and opens it in NOTEpad. Add the code given below to this file −
fn
main(){
   println!("Rust says Hello to TutorialsPoint !!");
}
The above program defines a function main fn main(). The fn keyword is used to define a function. The main() is a predefined function that acts as an entry point to the program. println! is a predefined macro in Rust. It is used to print a string (here Hello) to the console. Macro calls are always marked with an exclamation mark – !.
Compile the Hello.rs file using rustc.
Compile the Hello.rs file using rustc.
C:\Users\Admin\HelloWorld-App>rustc Hello.rs

Upon successful compilation of the program, an executable file (file_name.exe) is generated. To verify if the .exe file is generated, execute the following command.
C:\Users\Admin\HelloWorld-App>dir
//lists the files in folder
Hello.exe
Hello.pdb
Hello.rs

Execute the Hello.exe file and verify the output.
Rust provides a powerful macro system that allows meta-programming. As you have seen in the previous example, macros look like functions, except that their name ends with a bang(!), but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program. Therefore, they provide more runtime features to a program unlike functions. Macros are an extended version of functions.
println!(); // prints just a newline
println!("hello ");//prints hello
println!("format {} arguments", "some"); //prints format some arguments

Comments are a way to improve the readability of a program. Comments can be used to include additional information about a program like author of the code, hints about a function/ construct, etc. The compiler ignores comments.
Rust supports the following types of comments −
Single-line comments ( // ) − Any text between a // and the end of a line is treated as a comment
Single-line comments ( // ) − Any text between a // and the end of a line is treated as a comment
Multi-line comments (/* */) − These comments may span multiple lines.
Multi-line comments (/* */) − These comments may span multiple lines.
//this is single line comment

/* This is a
   Multi-line comment
*/

Rust programs can be executed online through Tutorialspoint Coding Ground. Write the HelloWorld program in the Editor tab and click Execute to view result.
The Type System represents the different types of values supported by the language. The Type System checks validity of the supplied values, before they are stored or manipulated by the program. This ensures that the code behaves as expected. The Type System further allows for richer code hinting and automated documentation too.
Rust is a statically typed language. Every value in Rust is of a certain data type. The compiler can automatically infer data type of the variable based on the value assigned to it.
Use the let keyword to declare a variable.
fn main() {
   let company_string = "TutorialsPoint";  // string type
   let rating_float = 4.5;                 // float type
   let is_growing_boolean = true;          // boolean type
   let icon_char = '♥';                    //unicode character type

   println!("company name is:{}",company_string);
   println!("company rating on 5 is:{}",rating_float);
   println!("company is growing :{}",is_growing_boolean);
   println!("company icon is:{}",icon_char);
}
In the above example, data type of the variables will be inferred from the values assigned to them. For example, Rust will assign string data type to the variable company_string, float data type to rating_float, etc.
The println! macro takes two arguments −
A special syntax { }, which is the placeholder
The variable name or a constant
The placeholder will be replaced by the variable’s value
The output of the above code snippet will be −
company name is: TutorialsPoint
company rating on 5 is:4.5
company is growing: true
company icon is: ♥

A scalar type represents a single value. For example, 10,3.14,'c'. Rust has four primary scalar types.
Integer
Floating-point
Booleans
Characters
We will learn about each type in our subsequent sections.
An integer is a number without a fractional component. Simply put, the integer data type is used to represent whole numbers.
Integers can be further classified as Signed and Unsigned. Signed integers can store both negative and positive values. Unsigned integers can only store positive values. A detailed description if integer types is given below −
The size of an integer can be arch. This means the size of the data type will be derived from the architecture of the machine. An integer the size of which is arch will be 32 bits on an x86 machine and 64 bits on an x64 machine. An arch integer is primarily used when indexing some sort of collection.
fn main() {
   let result = 10;    // i32 by default
   let age:u32 = 20;
   let sum:i32 = 5-15;
   let mark:isize = 10;
   let count:usize = 30;
   println!("result value is {}",result);
   println!("sum is {} and age is {}",sum,age);
   println!("mark is {} and count is {}",mark,count);
}
The output will be as given below −
result value is 10
sum is -10 and age is 20
mark is 10 and count is 30

The above code will return a compilation error if you replace the value of age with a floating-point value.
Each signed variant can store numbers from -(2^(n-1) to 2^(n-1) -1, where n is the number of bits that variant uses. For example, i8 can store numbers from -(2^7) to 2^7 -1 − here we replaced n with 8.
Each unsigned variant can store numbers from 0 to (2^n)-1. For example, u8 can store numbers from 0 to (2^8)-1, which is equal to 0 to 255.
An integer overflow occurs when the value assigned to an integer variable exceeds the Rust defined range for the data type. Let us understand this with an example −
fn main() {
   let age:u8 = 255;

   // 0 to 255 only allowed for u8
   let weight:u8 = 256;   //overflow value is 0
   let height:u8 = 257;   //overflow value is 1
   let score:u8 = 258;    //overflow value is 2

   println!("age is {} ",age);
   println!("weight is {}",weight);
   println!("height is {}",height);
   println!("score is {}",score);
}
The valid range of unsigned u8 variable is 0 to 255. In the above example, the variables are assigned values greater than 255 (upper limit for an integer variable in Rust). On execution, the above code will return a warning − warning − literal out of range for u8 for weight, height and score variables. The overflow values after 255 will start from 0, 1, 2, etc. The final output without warning is as shown below −
age is 255
weight is 0
height is 1
score is 2

Float data type in Rust can be classified as f32 and f64. The f32 type is a single-precision float, and f64 has double precision. The default type is f64. Consider the following example to understand more about the float data type.
fn main() {
   let result = 10.00;        //f64 by default
   let interest:f32 = 8.35;
   let cost:f64 = 15000.600;  //double precision
   
   println!("result value is {}",result);
   println!("interest is {}",interest);
   println!("cost is {}",cost);
}
The output will be as shown below −
interest is 8.35
cost is 15000.6

Automatic type casting is not allowed in Rust. Consider the following code snippet. An integer value is assigned to the float variable interest.
fn main() {
   let interest:f32 = 8;   // integer assigned to float variable
   println!("interest is {}",interest);
}
The compiler throws a mismatched types error as given below.
error[E0308]: mismatched types
   --> main.rs:2:22
   |
 2 | let interest:f32=8;
   |    ^ expected f32, found integral variable
   |
   = note: expected type `f32`
      found type `{integer}`
error: aborting due to previous error(s)

For easy readability of large numbers, we can use a visual separator _ underscore to separate digits. That is 50,000 can be written as 50_000 . This is shown in the below example.
fn main() {
   let float_with_separator = 11_000.555_001;
   println!("float value {}",float_with_separator);
   
   let int_with_separator = 50_000;
   println!("int value {}",int_with_separator);
}
The output is given below −
float value 11000.555001
int value 50000

Boolean types have two possible values – true or false. Use the bool keyword to declare a boolean variable.
fn main() {
   let isfun:bool = true;
   println!("Is Rust Programming Fun ? {}",isfun);
}
The output of the above code will be −
Is Rust Programming Fun ? true

The character data type in Rust supports numbers, alphabets, Unicode and special characters. Use the char keyword to declare a variable of character data type. Rust’s char type represents a Unicode Scalar Value, which means it can represent a lot more than just ASCII. Unicode Scalar Values range from U+0000 to U+D7FF and U+E000 to U+10FFFF inclusive.
Let us consider an example to understand more about the Character data type.
fn main() {
   let special_character = '@'; //default
   let alphabet:char = 'A';
   let emoji:char = '😁';
   
   println!("special character is {}",special_character);
   println!("alphabet is {}",alphabet);
   println!("emoji is {}",emoji);
}
The output of the above code will be −
special character is @
alphabet is A
emoji is 😁

A variable is a named storage that programs can manipulate. Simply put, a variable helps programs to store values. Variables in Rust are associated with a specific data type. The data type determines the size and layout of the variable's memory, the range of values that can be stored within that memory and the set of operations that can be performed on the variable.
In this section, we will learn about the different rules for naming a variable.
The name of a variable can be composed of letters, digits, and the underscore character.
The name of a variable can be composed of letters, digits, and the underscore character.
It must begin with either a letter or an underscore.
It must begin with either a letter or an underscore.
Upper and lowercase letters are distinct because Rust is case-sensitive.
Upper and lowercase letters are distinct because Rust is case-sensitive.
The data type is optional while declaring a variable in Rust. The data type is inferred from the value assigned to the variable.
The syntax for declaring a variable is given below.
let variable_name = value;            // no type specified
let variable_name:dataType = value;   //type specified

fn main() {
   let fees = 25_000;
   let salary:f64 = 35_000.00;
   println!("fees is {} and salary is {}",fees,salary);
}
The output of the above code will be fees is 25000 and salary is 35000.
By default, variables are immutable − read only in Rust. In other words, the variable's value cannot be changed once a value is bound to a variable name.
Let us understand this with an example.
fn main() {
   let fees = 25_000;
   println!("fees is {} ",fees);
   fees = 35_000;
   println!("fees changed is {}",fees);
}
The output will be as shown below −
error[E0384]: re-assignment of immutable variable `fees`
 --> main.rs:6:3
   |
 3 | let fees = 25_000;
   | ---- first assignment to `fees`
...
 6 | fees=35_000;
   | ^^^^^^^^^^^ re-assignment of immutable variable

error: aborting due to previous error(s)

The error message indicates the cause of the error – you cannot assign values twice to immutable variable fees. This is one of the many ways Rust allows programmers to write code and takes advantage of the safety and easy concurrency.
Variables are immutable by default. Prefix the variable name with mut keyword to make it mutable. The value of a mutable variable can be changed.
The syntax for declaring a mutable variable is as shown below −
let mut variable_name = value;
let mut variable_name:dataType = value;
Let us understand this with an example

fn main() {
   let mut fees:i32 = 25_000;
   println!("fees is {} ",fees);
   fees = 35_000;
   println!("fees changed is {}",fees);
}
The output of the snippet is given below −
fees is 25000
fees changed is 35000

Constants represent values that cannot be changed. If you declare a constant then there is no way its value changes. The keyword for using constants is const. Constants must be explicitly typed. Following is the syntax to declare a constant.
const VARIABLE_NAME:dataType = value;

The naming convention for Constants are similar to that of variables. All characters in a constant name are usually in uppercase. Unlike declaring variables, the let keyword is not used to declare a constant.
We have used constants in Rust in the example below −
fn main() {
   const USER_LIMIT:i32 = 100;    // Declare a integer constant
   const PI:f32 = 3.14;           //Declare a float constant

   println!("user limit is {}",USER_LIMIT);  //Display value of the constant
   println!("pi value is {}",PI);            //Display value of the constant
}
In this section, we will learn about the differentiating factors between constants and variables.
Constants are declared using the const keyword while variables are declared using the let keyword.
Constants are declared using the const keyword while variables are declared using the let keyword.
A variable declaration can optionally have a data type whereas a constant declaration must specify the data type. This means const USER_LIMIT=100 will result in an error.
A variable declaration can optionally have a data type whereas a constant declaration must specify the data type. This means const USER_LIMIT=100 will result in an error.
A variable declared using the let keyword is by default immutable. However, you have an option to mutate it using the mut keyword. Constants are immutable.
A variable declared using the let keyword is by default immutable. However, you have an option to mutate it using the mut keyword. Constants are immutable.
Constants can be set only to a constant expression and not to the result of a function call or any other value that will be computed at runtime.
Constants can be set only to a constant expression and not to the result of a function call or any other value that will be computed at runtime.
Constants can be declared in any scope, including the global scope, which makes them useful for values that many parts of the code need to know about.
Constants can be declared in any scope, including the global scope, which makes them useful for values that many parts of the code need to know about.
Rust allows programmers to declare variables with the same name. In such a case, the new variable overrides the previous variable.
Let us understand this with an example.
fn main() {
   let salary = 100.00;
   let salary = 1.50 ; 
   // reads first salary
   println!("The value of salary is :{}",salary);
}
The above code declares two variables by the name salary. The first declaration is assigned a 100.00 while the second declaration is assigned value 1.50. The second variable shadows or hides the first variable while displaying output.
The value of salary is :1.50

Rust supports variables with different data types while shadowing.
Consider the following example.
The code declares two variables by the name uname. The first declaration is assigned a string value, whereas the second declaration is assigned an integer. The len function returns the total number of characters in a string value.
fn main() {
   let uname = "Mohtashim";
   let uname = uname.len();
   println!("name changed to integer : {}",uname);
}
name changed to integer: 9

Unlike variables, constants cannot be shadowed. If variables in the above program are replaced with constants, the compiler will throw an error.
fn main() {
   const NAME:&str = "Mohtashim";
   const NAME:usize = NAME.len(); 
   //Error : `NAME` already defined
   println!("name changed to integer : {}",NAME);
}
The String data type in Rust can be classified into the following −
String Literal(&str)
String Literal(&str)
String Object(String)
String Object(String)
String literals (&str) are used when the value of a string is known at compile time. String literals are a set of characters, which are hardcoded into a variable. For example, let company="Tutorials Point". String literals are found in module std::str. String literals are also known as string slices.
The following example declares two string literals − company and location.
fn main() {
   let company:&str="TutorialsPoint";
   let location:&str = "Hyderabad";
   println!("company is : {} location :{}",company,location);
}
String literals are static by default. This means that string literals are guaranteed to be valid for the duration of the entire program. We can also explicitly specify the variable as static as shown below −
fn main() {
   let company:&'static str = "TutorialsPoint";
   let location:&'static str = "Hyderabad";
   println!("company is : {} location :{}",company,location);
}
The above program will generate the following output −
company is : TutorialsPoint location :Hyderabad

The String object type is provided in Standard Library. Unlike string literal, the string object type is not a part of the core language. It is defined as public structure in standard library pub struct String. String is a growable collection. It is mutable and UTF-8 encoded type. The String object type can be used to represent string values that are provided at runtime. String object is allocated in the heap.
To create a String object, we can use any of the following syntax −
String::new()

The above syntax creates an empty string
String::from()

This creates a string with some default value passed as parameter to the from() method.
The following example illustrates the use of a String object.
fn main(){
   let empty_string = String::new();
   println!("length is {}",empty_string.len());

   let content_string = String::from("TutorialsPoint");
   println!("length is {}",content_string.len());
}
The above example creates two strings − an empty string object using the new method and a string object from string literal using the from method.
The output is as shown below −
length is 0
length is 14

An empty string object is created using the new() method and its value is set to hello.
fn main(){
   let mut z = String::new();
   z.push_str("hello");
   println!("{}",z);
}
The above program generates the following output −
hello

To access all methods of String object, convert a string literal to object type using the to_string() function.
fn main(){
   let name1 = "Hello TutorialsPoint , 
   Hello!".to_string();
   println!("{}",name1);
}
The above program generates the following output −
Hello TutorialsPoint , Hello!

The replace() function takes two parameters − the first parameter is a string pattern to search for and the second parameter is the new value to be replaced. In the above example, Hello appears two times in the name1 string.
The replace function replaces all occurrences of the string Hello with Howdy.
fn main(){
   let name1 = "Hello TutorialsPoint , 
   Hello!".to_string();         //String object
   let name2 = name1.replace("Hello","Howdy");    //find and replace
   println!("{}",name2);
}
The above program generates the following output −
Howdy TutorialsPoint , Howdy!

The as_str() function extracts a string slice containing the entire string.
fn main() {
   let example_string = String::from("example_string");
   print_literal(example_string.as_str());
}
fn print_literal(data:&str ){
   println!("displaying string literal {}",data);
}
The above program generates the following output −
displaying string literal example_string

The push() function appends the given char to the end of this String.
fn main(){
   let mut company = "Tutorial".to_string();
   company.push('s');
   println!("{}",company);
}
The above program generates the following output −
Tutorials

The push_str() function appends a given string slice onto the end of a String.
fn main(){
   let mut company = "Tutorials".to_string();
   company.push_str(" Point");
   println!("{}",company);
}
The above program generates the following output −
Tutorials Point

The len() function returns the total number of characters in a string (including spaces).
fn main() {
   let fullname = " Tutorials Point";
   println!("length is {}",fullname.len());
}
The above program generates the following output −
length is 20

The trim() function removes leading and trailing spaces in a string. NOTE that this function will not remove the inline spaces.
fn main() {
   let fullname = " Tutorials Point \r\n";
   println!("Before trim ");
   println!("length is {}",fullname.len());
   println!();
   println!("After trim ");
   println!("length is {}",fullname.trim().len());
}
The above program generates the following output −
Before trim
length is 24

After trim
length is 15

The split_whitespace() splits the input string into different strings. It returns an iterator so we are iterating through the tokens as shown below −
fn main(){
   let msg = "Tutorials Point has good t
   utorials".to_string();
   let mut i = 1;
   
   for token in msg.split_whitespace(){
      println!("token {} {}",i,token);
      i+=1;
   }
}
token 1 Tutorials
token 2 Point
token 3 has
token 4 good
token 5 tutorials

The split() string method returns an iterator over substrings of a string slice, separated by characters matched by a pattern. The limitation of the split() method is that the result cannot be stored for later use. The collect method can be used to store the result returned by split() as a vector.
fn main() {
   let fullname = "Kannan,Sudhakaran,Tutorialspoint";

   for token in fullname.split(","){
      println!("token is {}",token);
   }

   //store in a Vector
   println!("\n");
   let tokens:Vec<&str>= fullname.split(",").collect();
   println!("firstName is {}",tokens[0]);
   println!("lastname is {}",tokens[1]);
   println!("company is {}",tokens[2]);
}
The above example splits the string fullname, whenever it encounters a comma (,).
token is Kannan
token is Sudhakaran
token is Tutorialspoint

firstName is Kannan
lastname is Sudhakaran
company is Tutorialspoint

Individual characters in a string can be accessed using the chars method. Let us consider an example to understand this.
fn main(){
   let n1 = "Tutorials".to_string();

   for n in n1.chars(){
      println!("{}",n);
   }
}
T
u
t
o
r
i
a
l
s

A string value can be appended to another string. This is called concatenation or interpolation. The result of string concatenation is a new string object. The + operator internally uses an add method. The syntax of the add function takes two parameters. The first parameter is self – the string object itself and the second parameter is a reference of the second string object. This is shown below −
//add function
add(self,&str)->String { 
   // returns a String object
}

fn main(){
   let n1 = "Tutorials".to_string();
   let n2 = "Point".to_string();

   let n3 = n1 + &n2; // n2 reference is passed
   println!("{}",n3);
}
The Output will be as given below
TutorialsPoint

The following example illustrates converting a number to a string object −
fn main(){
   let number = 2020;
   let number_as_string = number.to_string(); 
   
   // convert number to string
   println!("{}",number_as_string);
   println!("{}",number_as_string=="2020");
}
The Output will be as given below
2020
true

Another way to add to String objects together is using a macro function called format. The use of Format! is as shown below.
fn main(){
   let n1 = "Tutorials".to_string();
   let n2 = "Point".to_string();
   let n3 = format!("{} {}",n1,n2);
   println!("{}",n3);
}
The Output will be as given below
Tutorials Point

An operator defines some function that will be performed on the data. The data on which operators work are called operands. Consider the following expression −
7 + 5 = 12
Here, the values 7, 5, and 12 are operands, while + and = are operators.
The major operators in Rust can be classified as −
Arithmetic
Bitwise
Comparison
Logical
Bitwise
Conditional
Assume the values in variables a and b are 10 and 5 respectively.
Show Examples
NOTE − The ++ and -- operators are not supported in Rust.
Relational Operators test or define the kind of relationship between two entities. Relational operators are used to compare two or more values. Relational operators return a Boolean value − true or false.
Assume the value of A is 10 and B is 20.
Show Examples
Logical Operators are used to combine two or more conditions. Logical operators too, return a Boolean value. Assume the value of variable A is 10 and B is 20.
Show Examples
Assume variable A = 2 and B = 3.
Show Examples
Decision-making structures require that the programmer specify one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false.
Shown below is the general form of a typical decision-making structure found in most of the programming languages −
if statement
An if statement consists of a Boolean expression followed by one or more statements.
if...else statement
An if statement can be followed by an optional else statement, which executes when the Boolean expression is false.
else...if and nested ifstatement
You can use one if or else if statement inside another if or else if statement(s).
match statement
A match statement allows a variable to be tested against a list of values.
The if...else construct evaluates a condition before a block of code is executed.
if boolean_expression {
   // statement(s) will execute if the boolean expression is true
}

If the Boolean expression evaluates to true, then the block of code inside the if statement will be executed. If the Boolean expression evaluates to false, then the first set of code after the end of the if statement (after the closing curly brace) will be executed.
fn main(){
   let num:i32 = 5;
   if num > 0 {
      println!("number is positive") ;
   }
}
The above example will print number is positive as the condition specified by the if block is true.
An if can be followed by an optional else block. The else block will execute if the Boolean expression tested by the if statement evaluates to false.
if boolean_expression {
   // statement(s) will execute if the boolean expression is true
} else {
   // statement(s) will execute if the boolean expression is false
}

The if block guards the conditional expression. The block associated with the if statement is executed if the Boolean expression evaluates to true.
The if block may be followed by an optional else statement. The instruction block associated with the else block is executed if the expression evaluates to false.
fn main() {
   let num = 12;
   if num % 2==0 {
      println!("Even");
   } else {
      println!("Odd");
   }
}
The above example prints whether the value in a variable is even or odd. The if block checks the divisibility of the value by 2 to determine the same. Here is the output of the above code −
Even

The else...if ladder is useful to test multiple conditions. The syntax is as shown below −
if boolean_expression1 {
   //statements if the expression1 evaluates to true
} else if boolean_expression2 {
   //statements if the expression2 evaluates to true
} else {
   //statements if both expression1 and expression2 result to false
}

When using if...else...if and else statements, there are a few points to keep in mind.
An if can have zero or one else's and it must come after any else..if.
An if can have zero to many else..if and they must come before the else.
Once an else..if succeeds, none of the remaining else..if or else will be tested.
fn main() {
   let num = 2 ;
   if num > 0 {
      println!("{} is positive",num);
   } else if num < 0 {
      println!("{} is negative",num);
   } else {
      println!("{} is neither positive nor negative",num) ;
   }
}
The snippet displays whether the value is positive, negative or zero.
2 is positive

The match statement checks if a current value is matching from a list of values, this is very much similar to the switch statement in C language. In the first place, notice that the expression following the match keyword does not have to be enclosed in parentheses.
The syntax is as shown below.
let expressionResult = match variable_expression {
   constant_expr1 => {
      //statements;
   },
   constant_expr2 => {
      //statements;
   },
   _ => {
      //default
   }
};

In the example given below, state_code is matched with a list of values MH, KL, KA, GA − if any match is found, a string value is returned to variable state. If no match is found, the default case _ matches and value Unkown is returned.
fn main(){
   let state_code = "MH";
   let state = match state_code {
      "MH" => {println!("Found match for MH"); "Maharashtra"},
      "KL" => "Kerala",
      "KA" => "Karnadaka",
      "GA" => "Goa",
      _ => "Unknown"
   };
   println!("State name is {}",state);
}
Found match for MH
State name is Maharashtra

There may be instances, where a block of code needs to be executed repeatedly. In general, programming instructions are executed sequentially: The first statement in a function is executed first, followed by the second, and so on.
Programming languages provide various control structures that allow for more complicated execution paths.
A loop statement allows us to execute a statement or group of statements multiple times. Given below is the general form of a loop statement in most of the programming languages.
Rust provides different types of loops to handle looping requirements −
while
loop
for
A loop the number of iterations of which is definite/fixed is termed as a definite loop. The for loop is an implementation of a definite loop.
The for loop executes the code block for a specified number of times. It can be used to iterate over a fixed set of values, such as an array. The syntax of the for loop is as given below
for temp_variable in lower_bound..upper_bound {
   //statements
}

An example of a for loop is as shown below
fn main(){
   for x in 1..11{ // 11 is not inclusive
      if x==5 {
         continue;
      }
      println!("x is {}",x);
   }
}
NOTE: that the variable x is only accessible within the for block.
x is 1
x is 2
x is 3
x is 4
x is 6
x is 7
x is 8
x is 9
x is 10

An indefinite loop is used when the number of iterations in a loop is indeterminate or unknown.
Indefinite loops can be implemented using −
While
The while loop executes the instructions each time the condition specified evaluates to true
Loop
The loop is a while(true) indefinite loop
fn main(){
   let mut x = 0;
   while x < 10{
      x+=1;
      println!("inside loop x value is {}",x);
   }
   println!("outside loop x value is {}",x);
}
The output is as shown below −
inside loop x value is 1
inside loop x value is 2
inside loop x value is 3
inside loop x value is 4
inside loop x value is 5
inside loop x value is 6
inside loop x value is 7
inside loop x value is 8
inside loop x value is 9
inside loop x value is 10
outside loop x value is 10

fn main(){
   //while true

   let mut x = 0;
   loop {
      x+=1;
      println!("x={}",x);

      if x==15 {
         break;
      }
   }
}
The break statement is used to take the control out of a construct. Using break in a loop causes the program to exit the loop.
x=1
x=2
x=3
x=4
x=5
x=6
x=7
x=8
x=9
x=10
x=11
x=12
x=13
x=14
x=15

The continue statement skips the subsequent statements in the current iteration and takes the control back to the beginning of the loop. Unlike the break statement, the continue does not exit the loop. It terminates the current iteration and starts the subsequent iteration.
An example of the continue statement is given below.
fn main() {

   let mut count = 0;

   for num in 0..21 {
      if num % 2==0 {
         continue;
      }
      count+=1;
   }
   println! (" The count of odd values between 0 and 20 is: {} ",count);
   //outputs 10
}
The above example displays the number of even values between 0 and 20. The loop exits the current iteration if the number is even. This is achieved using the continue statement.
The count of odd values between 0 and 20 is 10
Functions are the building blocks of readable, maintainable, and reusable code. A function is a set of statements to perform a specific task. Functions organize the program into logical blocks of code. Once defined, functions may be called to access code. This makes the code reusable. Moreover, functions make it easy to read and maintain the program’s code.
A function declaration tells the compiler about a function's name, return type, and parameters. A function definition provides the actual body of the function.
Defining a function
A function definition specifies what and how a specific task would be done.
Calling or invoking a Function
A function must be called so as to execute it.
Returning Functions
Functions may also return value along with control, back to the caller.
Parameterized Function
Parameters are a mechanism to pass values to functions.
A function definition specifies what and how a specific task would be done. Before using a function, it must be defined. The function body contains code that should be executed by the function. The rules for naming a function are similar to that of a variable. Functions are defined using the fn keyword. The syntax for defining a standard function is given below
fn function_name(param1,param2..paramN) {
   // function body
}

A function declaration can optionally contain parameters/arguments. Parameters are used to pass values to functions.
//Defining a function
fn fn_hello(){
   println!("hello from function fn_hello ");
}
A function must be called so as to execute it. This process is termed as function invocation. Values for parameters should be passed when a function is invoked. The function that invokes another function is called the caller function.
function_name(val1,val2,valN)

fn main(){
   //calling a function
   fn_hello();
}
Here, the main() is the caller function.
The following example defines a function fn_hello(). The function prints a message to the console. The main() function invokes the fn_hello() function.
fn main(){
   //calling a function
   fn_hello();
}
//Defining a function
fn fn_hello(){
   println!("hello from function fn_hello ");
}
hello from function fn_hello

Functions may also return a value along with control, back to the caller. Such functions are called returning functions.
Either of the following syntax can be used to define a function with return type.
// Syntax1
function function_name() -> return_type {
   //statements
   return value;
}

//Syntax2
function function_name() -> return_type {
   value //no semicolon means this value is returned
}

fn main(){
   println!("pi value is {}",get_pi());
}
fn get_pi()->f64 {
   22.0/7.0
}
pi value is 3.142857142857143

Parameters are a mechanism to pass values to functions. Parameters form a part of the function’s signature. The parameter values are passed to the function during its invocation. Unless explicitly specified, the number of values passed to a function must match the number of parameters defined.
Parameters can be passed to a function using one of the following techniques −
When a method is invoked, a new storage location is created for each value parameter. The values of the actual parameters are copied into them. Hence, the changes made to the parameter inside the invoked method have no effect on the argument.
The following example declares a variable no, which is initially 5. The variable is passed as parameter (by value) to the mutate_no_to_zero()functionnction, which changes the value to zero. After the function call when control returns back to main method the value will be the same.
fn main(){
   let no:i32 = 5;
   mutate_no_to_zero(no);
   println!("The value of no is:{}",no);
}

fn mutate_no_to_zero(mut param_no: i32) {
   param_no = param_no*0;
   println!("param_no value is :{}",param_no);
}
Output
param_no value is :0
The value of no is:5

When you pass parameters by reference, unlike value parameters, a new storage location is not created for these parameters. The reference parameters represent the same memory location as the actual parameters that are supplied to the method. Parameter values can be passed by reference by prefixing the variable name with an & . 
In the example given below, we have a variable no, which is initially 5. A reference to the variable no is passed to the mutate_no_to_zero() function. The function operates on the original variable. After the function call, when control returns back to main method, the value of the original variable will be the zero.
fn main() {
   let mut no:i32 = 5;
   mutate_no_to_zero(&mut no);
   println!("The value of no is:{}",no);
}
fn mutate_no_to_zero(param_no:&mut i32){
   *param_no = 0; //de reference
}
The * operator is used to access value stored in the memory location that the variable param_no points to. This is also known as dereferencing.
The output will be −
The value of no is 0.

The main() function passes a string object to the display() function.
fn main(){
   let name:String = String::from("TutorialsPoint");
   display(name); 
   //cannot access name after display
}
fn display(param_name:String){
   println!("param_name value is :{}",param_name);
}
param_name value is :TutorialsPoint

Tuple is a compound data type. A scalar type can store only one type of data. For example, an i32 variable can store only a single integer value. In compound types, we can store more than one value at a time and it can be of different types.
Tuples have a fixed length - once declared they cannot grow or shrink in size. The tuple index starts from 0.
//Syntax1
let tuple_name:(data_type1,data_type2,data_type3) = (value1,value2,value3);

//Syntax2
let tuple_name = (value1,value2,value3);

The following example displays the values in a tuple.
fn main() {
   let tuple:(i32,f64,u8) = (-325,4.9,22);
   println!("{:?}",tuple);
}
The println!("{ }",tuple) syntax cannot be used to display values in a tuple. This is because a tuple is a compound type. Use the println!("{:?}", tuple_name) syntax to print values in a tuple.
(-325, 4.9, 22)

The following example prints individual values in a tuple.
fn main() {
   let tuple:(i32,f64,u8) = (-325,4.9,22);
   println!("integer is :{:?}",tuple.0);
   println!("float is :{:?}",tuple.1);
   println!("unsigned integer is :{:?}",tuple.2);
}
integer is :-325
float is :4.9
unsigned integer is :2

The following example passes a tuple as parameter to a function. Tuples are passed by value to functions.
fn main(){
   let b:(i32,bool,f64) = (110,true,10.9);
   print(b);
}
//pass the tuple as a parameter

fn print(x:(i32,bool,f64)){
   println!("Inside print method");
   println!("{:?}",x);
}
Inside print method
(110, true, 10.9)

Destructing assignment is a feature of rust wherein we unpack the values of a tuple. This is achieved by assigning a tuple to distinct variables.
Consider the following example −
fn main(){
   let b:(i32,bool,f64) = (30,true,7.9);
   print(b);
}
fn print(x:(i32,bool,f64)){
   println!("Inside print method");
   let (age,is_male,cgpa) = x; //assigns a tuple to 
   distinct variables
   println!("Age is {} , isMale? {},cgpa is 
   {}",age,is_male,cgpa);
}
Variable x is a tuple which is assigned to the let statement. Each variable - age, is_male and cgpa will contain the corresponding values in a tuple.
Inside print method
Age is 30 , isMale? true,cgpa is 7.9

In this chapter, we will learn about an array and the various features associated with it. Before we learn about arrays, let us see how an array is different from a variable.
Variables have the following limitations −
Variables are scalar in nature. In other words, a variable declaration can only contain a single value at a time. This means that to store n values in a program n variable declaration will be needed. Hence, the use of variables is not feasible when one needs to store a larger collection of values.
Variables are scalar in nature. In other words, a variable declaration can only contain a single value at a time. This means that to store n values in a program n variable declaration will be needed. Hence, the use of variables is not feasible when one needs to store a larger collection of values.
Variables in a program are allocated memory in random order, thereby making it difficult to retrieve/read the values in the order of their declaration.
Variables in a program are allocated memory in random order, thereby making it difficult to retrieve/read the values in the order of their declaration.
An array is a homogeneous collection of values. Simply put, an array is a collection of values of the same data type.
The features of an array are as listed below −
An array declaration allocates sequential memory blocks.
An array declaration allocates sequential memory blocks.
Arrays are static. This means that an array once initialized cannot be resized.
Arrays are static. This means that an array once initialized cannot be resized.
Each memory block represents an array element.
Each memory block represents an array element.
Array elements are identified by a unique integer called the subscript/ index of the element.
Array elements are identified by a unique integer called the subscript/ index of the element.
Populating the array elements is known as array initialization.
Populating the array elements is known as array initialization.
Array element values can be updated or modified but cannot be deleted.
Array element values can be updated or modified but cannot be deleted.
Use the syntax given below to declare and initialize an array in Rust.
//Syntax1
let variable_name = [value1,value2,value3];

//Syntax2
let variable_name:[dataType;size] = [value1,value2,value3];

//Syntax3
let variable_name:[dataType;size] = [default_value_for_elements,size];

In the first syntax, type of the array is inferred from the data type of the array’s first element during initialization.
The following example explicitly specifies the size and the data type of the array. The {:?} syntax of the println!() function is used to print all values in the array. The len() function is used to compute the size of the array.
fn main(){
   let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}
array is [10, 20, 30, 40]
array size is :4

The following program declares an array of 4 elements. The datatype is not explicitly specified during the variable declaration. In this case, the array will be of type integer. The len() function is used to compute the size of the array.
fn main(){
   let arr = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}
array is [10, 20, 30, 40]
array size is :4

The following example creates an array and initializes all its elements with a default value of -1.
fn main() {
   let arr:[i32;4] = [-1;4];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}
array is [-1, -1, -1, -1]
array size is :4

The following example iterates through an array and prints the indexes and their corresponding values. The loop retrieves values from index 0 to 4 (index of the last array element).
fn main(){
   let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());

   for index in 0..4 {
      println!("index is: {} & value is : {}",index,arr[index]);
   }
}
array is [10, 20, 30, 40]
array size is :4
index is: 0 & value is : 10
index is: 1 & value is : 20
index is: 2 & value is : 30
index is: 3 & value is : 40

The iter() function fetches values of all elements in an array.
fn main(){

let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());

   for val in arr.iter(){
      println!("value is :{}",val);
   }
}
array is [10, 20, 30, 40]
array size is :4
value is :10
value is :20
value is :30
value is :40

The mut keyword can be used to declare a mutable array. The following example declares a mutable array and modifies value of the second array element.
fn main(){
   let mut arr:[i32;4] = [10,20,30,40];
   arr[1] = 0;
   println!("{:?}",arr);
}
[10, 0, 30, 40]

An array can be passed by value or by reference to functions.
fn main() {
   let arr = [10,20,30];
   update(arr);

   print!("Inside main {:?}",arr);
}
fn update(mut arr:[i32;3]){
   for i in 0..3 {
      arr[i] = 0;
   }
   println!("Inside update {:?}",arr);
}
Inside update [0, 0, 0]
Inside main [10, 20, 30]

fn main() {
   let mut arr = [10,20,30];
   update(&mut arr);
   print!("Inside main {:?}",arr);
}
fn update(arr:&mut [i32;3]){
   for i in 0..3 {
      arr[i] = 0;
   }
   println!("Inside update {:?}",arr);
}
Inside update [0, 0, 0]
Inside main [0, 0, 0]

Let us consider an example given below to understand array declaration and constants.
fn main() {
   let N: usize = 20;
   let arr = [0; N]; //Error: non-constant used with constant
   print!("{}",arr[10])
}
The compiler will result in an exception. This is because an array's length must be known at compile time. Here, the value of the variable "N" will be determined at runtime. In other words, variables cannot be used to define the size of an array.
However, the following program is valid −
fn main() {
   const N: usize = 20; 
   // pointer sized
   let arr = [0; N];

   print!("{}",arr[10])
}
The value of an identifier prefixed with the const keyword is defined at compile time and cannot be changed at runtime. usize is pointer-sized, thus its actual size depends on the architecture you are compiling your program for.
The memory for a program can be allocated in the following −
Stack
Heap
A stack follows a last in first out order. Stack stores data values for which the size is known at compile time. For example, a variable of fixed size i32 is a candidate for stack allocation. Its size is known at compile time. All scalar types can be stored in stack as the size is fixed.
Consider an example of a string, which is assigned a value at runtime. The exact size of such a string cannot be determined at compile time. So it is not a candidate for stack allocation but for heap allocation.
The heap memory stores data values the size of which is unknown at compile time. It is used to store dynamic data. Simply put, a heap memory is allocated to data values that may change throughout the life cycle of the program. The heap is an area in the memory which is less organized when compared to stack.
Each value in Rust has a variable that is called owner of the value. Every data stored in Rust will have an owner associated with it. For example, in the syntax − let age = 30, age is the owner of the value 30.
Each data can have only one owner at a time.
Each data can have only one owner at a time.
Two variables cannot point to the same memory location. The variables will always be pointing to different memory locations.
Two variables cannot point to the same memory location. The variables will always be pointing to different memory locations.
The ownership of value can be transferred by −
Assigning value of one variable to another variable.
Assigning value of one variable to another variable.
Passing value to a function.
Passing value to a function.
Returning value from a function.
Returning value from a function.
The key selling point of Rust as a language is its memory safety. Memory safety is achieved by tight control on who can use what and when restrictions.
Consider the following snippet −
fn main(){
   let v = vec![1,2,3]; 
   // vector v owns the object in heap

   //only a single variable owns the heap memory at any given time
   let v2 = v; 
   // here two variables owns heap value,
   //two pointers to the same content is not allowed in rust

   //Rust is very smart in terms of memory access ,so it detects a race condition
   //as two variables point to same heap

   println!("{:?}",v);
}
The above example declares a vector v. The idea of ownership is that only one variable binds to a resource, either v binds to resource or v2 binds to the resource. The above example throws an error − use of moved value: `v`. This is because the ownership of the resource is transferred to v2. It means the ownership is moved from v to v2 (v2=v) and v is invalidated after the move.
The ownership of a value also changes when we pass an object in the heap to a closure or function.
fn main(){
   let v = vec![1,2,3];     // vector v owns the object in heap
   let v2 = v;              // moves ownership to v2
   display(v2);             // v2 is moved to display and v2 is invalidated
   println!("In main {:?}",v2);    //v2 is No longer usable here
}
fn display(v:Vec<i32>){
   println!("inside display {:?}",v);
}
Ownership passed to the function will be invalidated as function execution completes. One work around for this is let the function return the owned object back to the caller.
fn main(){
   let v = vec![1,2,3];       // vector v owns the object in heap
   let v2 = v;                // moves ownership to v2
   let v2_return = display(v2);    
   println!("In main {:?}",v2_return);
}
fn display(v:Vec<i32>)->Vec<i32> { 
   // returning same vector
   println!("inside display {:?}",v);
}
In case of primitive types, contents from one variable is copied to another. So, there is no ownership move happening. This is because a primitive variable needs less resources than an object. Consider the following example −
fn main(){
   let u1 = 10;
   let u2 = u1;  // u1 value copied(not moved) to u2

   println!("u1 = {}",u1);
}
The output will be – 10.
It is very inconvenient to pass the ownership of a variable to another function and then return the ownership. Rust supports a concept, borrowing, where the ownership of a value is transferred temporarily to an entity and then returned to the original owner entity.
Consider the following −
fn main(){
   // a list of nos
   let v = vec![10,20,30];
   print_vector(v);
   println!("{}",v[0]); // this line gives error
}
fn print_vector(x:Vec<i32>){
   println!("Inside print_vector function {:?}",x);
}
The main function invokes a function print_vector(). A vector is passed as parameter to this function. The ownership of the vector is also passed to the print_vector() function from the main(). The above code will result in an error as shown below when the main() function tries to access the vector v.
|  print_vector(v);
|     - value moved here
|  println!("{}",v[0]);
|     ^ value used here after move

This is because a variable or value can no longer be used by the function that originally owned it once the ownership is transferred to another function.
When a function transfers its control over a variable/value to another function temporarily, for a while, it is called borrowing. This is achieved by passing a reference to the variable (& var_name) rather than passing the variable/value itself to the function. The ownership of the variable/ value is transferred to the original owner of the variable after the function to which the control was passed completes execution.
fn main(){
   // a list of nos
   let v = vec![10,20,30];
   print_vector(&v); // passing reference
   println!("Printing the value from main() v[0]={}",v[0]);
}
fn print_vector(x:&Vec<i32>){
   println!("Inside print_vector function {:?}",x);
}
Inside print_vector function [10, 20, 30]
Printing the value from main() v[0] = 10

A function can modify a borrowed resource by using a mutable reference to such resource. A mutable reference is prefixed with &mut. Mutable references can operate only on mutable variables.
fn add_one(e: &mut i32) {
   *e+= 1;
}
fn main() {
   let mut i = 3;
   add_one(&mut i);
   println!("{}", i);
}
The main() function declares a mutable integer variable i and passes a mutable reference of i to the add_one(). The add_one() increments the value of the variable i by one.
fn main() {
   let mut name:String = String::from("TutorialsPoint");
   display(&mut name); 
   //pass a mutable reference of name
   println!("The value of name after modification is:{}",name);
}
fn display(param_name:&mut String){
   println!("param_name value is :{}",param_name);
   param_name.push_str(" Rocks"); 
   //Modify the actual string,name
}
The main() function passes a mutable reference of the variable name to the display() function. The display function appends an additional string to the original name variable.
param_name value is :TutorialsPoint
The value of name after modification is:TutorialsPoint Rocks

A slice is a pointer to a block of memory. Slices can be used to access portions of data stored in contiguous memory blocks. It can be used with data structures like arrays, vectors and strings. Slices use index numbers to access portions of data. The size of a slice is determined at runtime.
Slices are pointers to the actual data. They are passed by reference to functions, which is also known as borrowing.
For example, slices can be used to fetch a portion of a string value. A sliced string is a pointer to the actual string object. Therefore, we need to specify the starting and ending index of a String. Index starts from 0 just like arrays.
let sliced_value = &data_structure[start_index..end_index]

The minimum index value is 0 and the maximum index value is the size of the data structure. NOTE that the end_index will not be included in final string.
The diagram below shows a sample string Tutorials, that has 9 characters. The index of the first character is 0 and that of the last character is 8.
The following code fetches 5 characters from the string (starting from index 4).
fn main() {
   let n1 = "Tutorials".to_string();
   println!("length of string is {}",n1.len());
   let c1 = &n1[4..9]; 
   
   // fetches characters at 4,5,6,7, and 8 indexes
   println!("{}",c1);
}
length of string is 9
rials

The main() function declares an array with 5 elements. It invokes the use_slice() function and passes to it a slice of three elements (points to the data array). The slices are passed by reference. The use_slice() function prints the value of the slice and its length.
fn main(){
   let data = [10,20,30,40,50];
   use_slice(&data[1..4]);
   //this is effectively borrowing elements for a while
}
fn use_slice(slice:&[i32]) { 
   // is taking a slice or borrowing a part of an array of i32s
   println!("length of slice is {:?}",slice.len());
   println!("{:?}",slice);
}
length of slice is 3
[20, 30, 40]

The &mut  keyword can be used to mark a slice as mutable.
fn main(){
   let mut data = [10,20,30,40,50];
   use_slice(&mut data[1..4]);
   // passes references of 
   20, 30 and 40
   println!("{:?}",data);
}
fn use_slice(slice:&mut [i32]) {
   println!("length of slice is {:?}",slice.len());
   println!("{:?}",slice);
   slice[0] = 1010; // replaces 20 with 1010
}
length of slice is 3
[20, 30, 40]
[10, 1010, 30, 40, 50]

The above code passes a mutable slice to the use_slice() function. The function modifies the second element of the original array.
Arrays are used to represent a homogeneous collection of values. Similarly, a structure is another user defined data type available in Rust that allows us to combine data items of different types, including another structure. A structure defines data as a key-value pair.
The struct keyword is used to declare a structure. Since structures are statically typed, every field in the structure must be associated with a data type. The naming rules and conventions for a structure is like that of a variable. The structure block must end with a semicolon.
struct Name_of_structure {
   field1:data_type,
   field2:data_type,
   field3:data_type
}

After declaring a struct, each field should be assigned a value. This is known as initialization.
let instance_name = Name_of_structure {
   field1:value1,
   field2:value2,
   field3:value3
}; 
//NOTE the semicolon
Syntax: Accessing values in a structure
Use the dot notation to access value of a specific field.
instance_name.field1
Illustration
struct Employee {
   name:String,
   company:String,
   age:u32
}
fn main() {
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   println!("Name is :{} company is {} age is {}",emp1.name,emp1.company,emp1.age);
}
The above example declares a struct Employee with three fields – name, company and age of types. The main() initializes the structure. It uses the println! macro to print values of the fields defined in the structure.
Name is :Mohtashim company is TutorialsPoint age is 50

To modify an instance, the instance variable should be marked mutable. The below example declares and initializes a structure named Employee and later modifies value of the age field to 40 from 50.
let mut emp1 = Employee {
   company:String::from("TutorialsPoint"),
   name:String::from("Mohtashim"),
   age:50
};
emp1.age = 40;
println!("Name is :{} company is {} age is 
{}",emp1.name,emp1.company,emp1.age);
Name is :Mohtashim company is TutorialsPoint age is 40

The following example shows how to pass instance of struct as a parameter. The display method takes an Employee instance as parameter and prints the details.
fn display( emp:Employee) {
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}

Here is the complete program −
//declare a structure
struct Employee {
   name:String,
   company:String,
   age:u32
}
fn main() {
   //initialize a structure
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   //pass emp1 and emp2 to display()
   display(emp1);
   display(emp2);
}
// fetch values of specific structure fields using the 
// operator and print it to the console
fn display( emp:Employee){
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}
Name is :Mohtashim company is TutorialsPoint age is 50
Name is :Kannan company is TutorialsPoint age is 32

Let us consider a function who_is_elder(), which compares two employees age and returns the elder one.
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
   if emp1.age>emp2.age {
      return emp1;
   } else {
      return emp2;
   }
}
Here is the complete program −
fn main() {
   //initialize structure
   let emp1 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   let elder = who_is_elder(emp1,emp2);
   println!("elder is:");

   //prints details of the elder employee
   display(elder);
}
//accepts instances of employee structure and compares their age
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
   if emp1.age>emp2.age {
      return emp1;
   } else {
      return emp2;
   }
}
//display name, comapny and age of the employee
fn display( emp:Employee) {
   println!("Name is :{} company is {} age is {}",emp.name,emp.company,emp.age);
}
//declare a structure
struct Employee {
   name:String,
   company:String,
   age:u32
}
elder is:
Name is :Mohtashim company is TutorialsPoint age is 50

Methods are like functions. They are a logical group of programming instructions. Methods are declared with the fn keyword. The scope of a method is within the structure block.
Methods are declared outside the structure block. The impl keyword is used to define a method within the context of a structure. The first parameter of a method will be always self, which represents the calling instance of the structure. Methods operate on the data members of a structure.
To invoke a method, we need to first instantiate the structure. The method can be called using the structure's instance.
struct My_struct {}
impl My_struct { 
   //set the method's context
   fn method_name() { 
      //define a method
   }
}

The following example defines a structure Rectangle with fields − width and height. A method area is defined within the structure's context. The area method accesses the structure's fields via the self keyword and calculates the area of a rectangle.
//define dimensions of a rectangle
struct Rectangle {
   width:u32, height:u32
}

//logic to calculate area of a rectangle
impl Rectangle {
   fn area(&self)->u32 {
      //use the . operator to fetch the value of a field via the self keyword
      self.width * self.height
   }
}

fn main() {
   // instanatiate the structure
   let small = Rectangle {
      width:10,
      height:20
   };
   //print the rectangle's area
   println!("width is {} height is {} area of Rectangle 
   is {}",small.width,small.height,small.area());
}
width is 10 height is 20 area of Rectangle is 200

Static methods can be used as utility methods. These methods exist even before the structure is instantiated. Static methods are invoked using the structure's name and can be accessed without an instance. Unlike normal methods, a static method will not take the &self parameter.
A static method like functions and other methods can optionally contain parameters.
impl Structure_Name {
   //static method that creates objects of the Point structure
   fn method_name(param1: datatype, param2: datatype) -> return_type {
      // logic goes here
   }
}
The structure_name :: syntax is used to access a static method.
structure_name::method_name(v1,v2)

The following example uses the getInstance method as a factory class that creates and returns instances of the structure Point.
//declare a structure
struct Point {
   x: i32,
   y: i32,
}
impl Point {
   //static method that creates objects of the Point structure
   fn getInstance(x: i32, y: i32) -> Point {
      Point { x: x, y: y }
   }
   //display values of the structure's field
   fn display(&self){
      println!("x ={} y={}",self.x,self.y );
   }
}
fn main(){
   // Invoke the static method
   let p1 = Point::getInstance(10,20);
   p1.display();
}
x =10 y=20

In Rust programming, when we have to select a value from a list of possible variants we use enumeration data types. An enumerated type is declared using the enum keyword. Following is the syntax of enum −
enum enum_name {
   variant1,
   variant2,
   variant3
}

The example declares an enum − GenderCategory, which has variants as Male and Female. The print! macro displays value of the enum. The compiler will throw an error the trait std::fmt::Debug is not implemented for GenderCategory. The attribute #[derive(Debug)] is used to suppress this error.
// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
   Male,Female
}
fn main() {
   let male = GenderCategory::Male;
   let female = GenderCategory::Female;

   println!("{:?}",male);
   println!("{:?}",female);
}

Male
Female

The following example defines a structure Person. The field gender is of the type GenderCategory (which is an enum) and can be assigned either Male or Female as value.
// The `derive` attribute automatically creates the 
implementation
// required to make this `enum` printable with 
`fmt::Debug`.

#[derive(Debug)]
enum GenderCategory {
   Male,Female
}

// The `derive` attribute automatically creates the implementation
// required to make this `struct` printable with `fmt::Debug`.
#[derive(Debug)]
struct Person {
   name:String,
   gender:GenderCategory
}

fn main() {
   let p1 = Person {
      name:String::from("Mohtashim"),
      gender:GenderCategory::Male
   };
   let p2 = Person {
      name:String::from("Amy"),
      gender:GenderCategory::Female
   };
   println!("{:?}",p1);
   println!("{:?}",p2);
}
The example creates objects p1 and p2 of type Person and initializes the attributes, name and gender for each of these objects.
Person { name: "Mohtashim", gender: Male }
Person { name: "Amy", gender: Female }

Option is a predefined enum in the Rust standard library. This enum has two values − Some(data) and None.
enum Option<T> {
   Some(T),      //used to return a value
   None          // used to return null, as Rust doesn't support 
   the null keyword
}

Here, the type T represents value of any type.
Rust does not support the null keyword. The value None, in the enumOption, can be used by a function to return a null value. If there is data to return, the function can return Some(data).
Let us understand this with an example −
The program defines a function is_even(), with a return type Option. The function verifies if the value passed is an even number. If the input is even, then a value true is returned, else the function returns None.
fn main() {
   let result = is_even(3);
   println!("{:?}",result);
   println!("{:?}",is_even(30));
}
fn is_even(no:i32)->Option<bool> {
   if no%2 == 0 {
      Some(true)
   } else {
      None
   }
}
None
Some(true)

The match statement can be used to compare values stored in an enum. The following example defines a function, print_size, which takes CarType enum as parameter. The function compares the parameter values with a pre-defined set of constants and displays the appropriate message.
enum CarType {
   Hatch,
   Sedan,
   SUV
}
fn print_size(car:CarType) {
   match car {
      CarType::Hatch => {
         println!("Small sized car");
      },
      CarType::Sedan => {
         println!("medium sized car");
      },
      CarType::SUV =>{
         println!("Large sized Sports Utility car");
      }
   }
}
fn main(){
   print_size(CarType::SUV);
   print_size(CarType::Hatch);
   print_size(CarType::Sedan);
}
Large sized Sports Utility car
Small sized car
medium sized car

The example of is_even function, which returns Option type, can also be implemented with match statement as shown below −
fn main() {
   match is_even(5) {
      Some(data) => {
         if data==true {
            println!("Even no");
         }
      },
      None => {
         println!("not even");
      }
   }
}
fn is_even(no:i32)->Option<bool> {
   if no%2 == 0 {
      Some(true)
   } else {
      None
   }
}
not even

It is possible to add data type to each variant of an enum. In the following example, Name and Usr_ID variants of the enum are of String and integer types respectively. The following example shows the use of match statement with an enum having a data type.
// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
   Name(String),Usr_ID(i32)
}
fn main() {
   let p1 = GenderCategory::Name(String::from("Mohtashim"));
   let p2 = GenderCategory::Usr_ID(100);
   println!("{:?}",p1);
   println!("{:?}",p2);

   match p1 {
      GenderCategory::Name(val)=> {
         println!("{}",val);
      }
      GenderCategory::Usr_ID(val)=> {
         println!("{}",val);
      }
   }
}
Name("Mohtashim")
Usr_ID(100)
Mohtashim

A logical group of code is called a Module. Multiple modules are compiled into a unit called crate. Rust programs may contain a binary crate or a library crate. A binary crate is an executable project that has a main() method. A library crate is a group of components that can be reused in other projects. Unlike a binary crate, a library crate does not have an entry point (main() method). The Cargo tool is used to manage crates in Rust. For example, the network module contains networking related functions and the graphics module contains drawing-related functions. Modules are similar to namespaces in other programming languages. Third-party crates can be downloaded using cargo from crates.io.
crate
Is a compilation unit in Rust; Crate is compiled to binary or library.
cargo
The official Rust package management tool for crates.
module
Logically groups code within a crate.
crates.io
The official Rust package registry.
//public module
pub mod a_public_module {
   pub fn a_public_function() {
      //public function
   }
   fn a_private_function() {
      //private function
   }
}
//private module
mod a_private_module {
   fn a_private_function() {
   }
}

Modules can be public or private. Components in a private module cannot be accessed by other modules. Modules in Rust are private by default. On the contrary, functions in a public module can be accessed by other modules. Modules should be prefixed with pub keyword to make it public. Functions within a public module must also be made public.
The example defines a public module − movies. The module contains a function play() that accepts a parameter and prints its value.
pub mod movies {
   pub fn play(name:String) {
      println!("Playing movie {}",name);
   }
}
fn main(){
   movies::play("Herold and Kumar".to_string());
}
Playing movie Herold and Kumar

The use keyword helps to import a public module.
use public_module_name::function_name;

pub mod movies {
   pub fn play(name:String) {
      println!("Playing movie {}",name);
   }
}
use movies::play;
fn main(){
   play("Herold and Kumar ".to_string());
}
Playing movie Herold and Kumar

Modules can also be nested. The comedy module is nested within the english module, which is further nested in the movies module. The example given below defines a function play inside the movies/english/comedy module.
pub mod movies {
   pub mod english {
      pub mod comedy {
         pub fn play(name:String) {
            println!("Playing comedy movie {}",name);
         }
      }
   }
}
use movies::english::comedy::play; 
// importing a public module

fn main() {
   // short path syntax
   play("Herold and Kumar".to_string());
   play("The Hangover".to_string());

   //full path syntax
   movies::english::comedy::play("Airplane!".to_string());
}
Playing comedy movie Herold and Kumar
Playing comedy movie The Hangover
Playing comedy movie Airplane!

Let us create a library crate named movie_lib, which contains a module movies. To build the movie_lib library crate, we will use the tool cargo.
Create a folder movie-app followed by a sub-folder movie-lib. After the folder and sub-folder are created, create an src folder and a Cargo.toml file in this directory. The source code should go in the src folder. Create the files lib.rs and movies.rs in the src folder. The Cargo.toml file will contain the metadata of the project like version number, author name, etc.
The project directory structure will be as shown below −
movie-app
   movie-lib/
      -->Cargo.toml
      -->src/
         lib.rs
         movies.rs

[package]
name = "movies_lib"
version = "0.1.0"
authors = ["Mohtashim"]

Add the following module definition to this file.
pub mod movies;

The above line creates a public module − movies.
This file will define all functions for the movies module.
pub fn play(name:String){
   println!("Playing movie {} :movies-app",name);
}

The above code defines a function play() that accepts a parameter and prints it to the console.
Build app using the cargo build command to verify if the library crate is structured properly. Make sure you are at root of project − the movie-app folder. The following message will be displayed in the terminal if the build succeeds.
D:\Rust\movie-lib> cargo build
   Compiling movies_lib v0.1.0 (file:///D:/Rust/movie-lib)
   Finished dev [unoptimized + debuginfo] target(s) in 0.67s

Create another folder movie-lib-test in the movie-app folder followed by a Cargo.toml file and the src folder. This project should have main method as this is a binary crate, which will consume the library crate created previously. Create a main.rs file in the src folder. The folder structure will be as shown.
movie-app
   movie-lib 
   // already completed

   movie-lib-test/
      -->Cargo.toml
      -->src/
         main.rs

[package]
name = "test_for_movie_lib"
version = "0.1.0"
authors = ["Mohtashim"]

[dependencies]
movies_lib = { path = "../movie-lib" }

NOTE − The path to the library folder is set as dependencies. The following diagram shows the contents of both the projects.
extern crate movies_lib;
use movies_lib::movies::play;
fn main() {
   println!("inside main of test ");
   play("Tutorialspoint".to_string())
}

The above code imports an external package called movies_lib. Check the Cargo.toml of current project to verify the crate name.
We will use the cargo build and cargo run to build the binary project and execute it as shown below −
Rust's standard collection library provides efficient implementations of the most common general-purpose programming data structures. This chapter discusses the implementation of the commonly used collections − Vector, HashMap and HashSet.
A Vector is a resizable array. It stores values in contiguous memory blocks. The predefined structure Vec can be used to create vectors. Some important features of a Vector are −
A Vector can grow or shrink at runtime.
A Vector can grow or shrink at runtime.
A Vector is a homogeneous collection.
A Vector is a homogeneous collection.
A Vector stores data as sequence of elements in a particular order. Every element in a Vector is assigned a unique index number. The index starts from 0 and goes up to n-1 where, n is the size of the collection. For example, in a collection of 5 elements, the first element will be at index 0 and the last element will be at index 4.
A Vector stores data as sequence of elements in a particular order. Every element in a Vector is assigned a unique index number. The index starts from 0 and goes up to n-1 where, n is the size of the collection. For example, in a collection of 5 elements, the first element will be at index 0 and the last element will be at index 4.
A Vector will only append values to (or near) the end. In other words, a Vector can be used to implement a stack.
A Vector will only append values to (or near) the end. In other words, a Vector can be used to implement a stack.
Memory for a Vector is allocated in the heap.
Memory for a Vector is allocated in the heap.
let mut instance_name = Vec::new();

The static method new() of the Vecstructure is used to create a vector instance.
Alternatively, a vector can also be created using the vec! macro. The syntax is as given below −
let vector_name = vec![val1,val2,val3]

The following table lists some commonly used functions of the Vec structure.
pub fn new()->Vect
Constructs a new, empty Vec. The vector will not allocate until elements are pushed onto it.
pub fn push(&mut self, value: T)
Appends an element to the back of a collection.
pub fn remove(&mut self, index: usize) -> T
Removes and returns the element at position index within the vector, shifting all elements after it to the left.
pub fn contains(&self, x: &T) -> bool
Returns true if the slice contains an element with the given value.
pub fn len(&self) -> usize
Returns the number of elements in the vector, also referred to as its 'length'.
To create a vector, we use the static method new−
fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);

   println!("size of vector is :{}",v.len());
   println!("{:?}",v);
}
The above example creates a Vector using the static method new() that is defined in structure Vec. The push(val) function appends the value passed as parameter to the collection. The len() function returns the length of the vector.
size of vector is :3
[20, 30, 40]

The following code creates a vector using the vec! macro. The data type of the vector is inferred the first value that is assigned to it.
fn main() {
   let v = vec![1,2,3];
   println!("{:?}",v);
}
[1, 2, 3]

As mentioned earlier, a vector can only contain values of the same data type. The following snippet will throw a error[E0308]: mismatched types error.
fn main() {
   let v = vec![1,2,3,"hello"];
   println!("{:?}",v);
}
Appends an element to the end of a collection.
fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);
   
   println!("{:?}",v);
}
[20, 30, 40]

Removes and returns the element at position index within the vector, shifting all elements after it to the left.
fn main() {
   let mut v = vec![10,20,30];
   v.remove(1);
   println!("{:?}",v);
}
[10, 30]

Returns true if the slice contains an element with the given value −
fn main() {
   let v = vec![10,20,30];
   if v.contains(&10) {
      println!("found 10");
   }
   println!("{:?}",v);
}
found 10
[10, 20, 30]

Returns the number of elements in the vector, also referred to as its 'length'.
fn main() {
   let v = vec![1,2,3];
   println!("size of vector is :{}",v.len());
}
size of vector is :3

Individual elements in a vector can be accessed using their corresponding index numbers. The following example creates a vector ad prints the value of the first element.
fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);

   println!("{:?}",v[0]);
}
Output: `20`
Values in a vector can also be fetched using reference to the collection.
fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);
   v.push(500);

   for i in &v {
      println!("{}",i);
   }
   println!("{:?}",v);
}
20
30
40
500
[20, 30, 40, 500]

A map is a collection of key-value pairs (called entries). No two entries in a map can have the same key. In short, a map is a lookup table. A HashMap stores the keys and values in a hash table. The entries are stored in an arbitrary order. The key is used to search for values in the HashMap. The HashMap structure is defined in the std::collections module. This module should be explicitly imported to access the HashMap structure.
let mut instance_name = HashMap::new();

The static method new() of the HashMap structure is used to create a HashMap object. This method creates an empty HashMap.
The commonly used functions of HashMap are discussed below −
pub fn insert(&mut self, k: K, v: V) -> Option
Inserts a key/value pair, if no key then None is returned. After update, old value is returned.
pub fn len(&self) -> usize
Returns the number of elements in the map.
pub fn get<Q: ?Sized>(&lself, k: &Q) -> Option<&V> where K:Borrow Q:Hash+ Eq
Returns a reference to the value corresponding to the key.
pub fn iter(&self) -> Iter<K, V>
An iterator visiting all key-value pairs in arbitrary order. The iterator element type is (&'a K, &'a V).
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
Returns true if the map contains a value for the specified key.
pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)>
Removes a key from the map, returning the stored key and value if the key was previously in the map.
Inserts a key/value pair into the HashMap.
use std::collections::HashMap;
fn main(){
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("{:?}",stateCodes);
}
The above program creates a HashMap and initializes it with 2 key-value pairs.
{"KL": "Kerala", "MH": "Maharashtra"}

Returns the number of elements in the map
use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("size of map is {}",stateCodes.len());
}
The above example creates a HashMap and prints the total number of elements in it.
size of map is 2

Returns a reference to the value corresponding to the key. The following example retrieves the value for key KL in the HashMap.
use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("size of map is {}",stateCodes.len());
   println!("{:?}",stateCodes);

   match stateCodes.get(&"KL") {
      Some(value)=> {
         println!("Value for key KL is {}",value);
      }
      None => {
         println!("nothing found");
      }
   }
}
size of map is 2
{"KL": "Kerala", "MH": "Maharashtra"}
Value for key KL is Kerala

Returns an iterator containing reference to all key-value pairs in an arbitrary order.
use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");

   for (key, val) in stateCodes.iter() {
      println!("key: {} val: {}", key, val);
   }
}
key: MH val: Maharashtra
key: KL val: Kerala

Returns true if the map contains a value for the specified key.
use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   stateCodes.insert("GJ","Gujarat");

   if stateCodes.contains_key(&"GJ") {
      println!("found key");
   }
}
found key

Removes a key from the map.
use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   stateCodes.insert("GJ","Gujarat");

   println!("length of the hashmap {}",stateCodes.len());
   stateCodes.remove(&"GJ");
   println!("length of the hashmap after remove() {}",stateCodes.len());
}
length of the hashmap 3
length of the hashmap after remove() 2

HashSet is a set of unique values of type T. Adding and removing values is fast, and it is fast to ask whether a given value is in the set or not. The HashSet structure is defined in the std::collections module. This module should be explicitly imported to access the HashSet structure.
let mut hash_set_name = HashSet::new();

The static method, new, of HashSet structure is used to create a HashSet. This method creates an empty HashSet.
The following table lists some of the commonly used methods of the HashSet structure.
pub fn insert(&mut self, value: T) -> bool
Adds a value to the set. If the set did not have this value present, true is returned else false.
pub fn len(&self) -> usize
Returns the number of elements in the set.
pub fn get<Q:?Sized>(&self, value: &Q) -> Option<&T> where T: Borrow,Q: Hash + Eq,
Returns a reference to the value in the set, if any that is equal to the given value.
pub fn iter(&self) -> Iter
Returns an iterator visiting all elements in arbitrary order. The iterator element type is &'a T.
pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
Returns true if the set contains a value.
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
Removes a value from the set. Returns true if the value was present in the set.
Adds a value to the set. A HashSet does not add duplicate values to the collection.
use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();

   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");//duplicates not added

   println!("{:?}",names);
}
{"TutorialsPoint", "Kannan", "Mohtashim"}

Returns the number of elements in the set.
use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   println!("size of the set is {}",names.len());
}
size of the set is 3

Retruns an iterator visiting all elements in arbitrary order.
use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");

   for name in names.iter() {
      println!("{}",name);
   }
}
TutorialsPoint
Mohtashim
Kannan

Returns a reference to the value in the set, if any, which is equal to the given value.
use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");

   match names.get(&"Mohtashim"){
      Some(value)=>{
         println!("found {}",value);
      }
      None =>{
         println!("not found");
      }
   }
   println!("{:?}",names);
}
found Mohtashim
{"Kannan", "Mohtashim", "TutorialsPoint"}

Returns true if the set contains a value.
use std::collections::HashSet;

fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");

   if names.contains(&"Kannan") {
      println!("found name");
   }  
}
found name

Removes a value from the set.
use std::collections::HashSet;

fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   println!("length of the Hashset: {}",names.len());
   names.remove(&"Kannan");
   println!("length of the Hashset after remove() : {}",names.len());
}
length of the Hashset: 3
length of the Hashset after remove() : 2

In Rust, errors can be classified into two major categories as shown in the table below.
Recoverable
Errors which can be handled
UnRecoverable
Errors which cannot be handled
A recoverable error is an error that can be corrected. A program can retry the failed operation or specify an alternate course of action when it encounters a recoverable error. Recoverable errors do not cause a program to fail abruptly. An example of a recoverable error is File Not Found error.
Unrecoverable errors cause a program to fail abruptly. A program cannot revert to its normal state if an unrecoverable error occurs. It cannot retry the failed operation or undo the error. An example of an unrecoverable error is trying to access a location beyond the end of an array.
Unlike other programming languages, Rust does not have exceptions. It returns an enum Result<T, E> for recoverable errors, while it calls the panic macro if the program encounters an unrecoverable error. The panic macro causes the program to exit abruptly.
panic! macro allows a program to terminate immediately and provide feedback to the caller of the program. It should be used when a program reaches an unrecoverable state.
fn main() {
   panic!("Hello");
   println!("End of main"); //unreachable statement
}
In the above example, the program will terminate immediately when it encounters the panic! macro.
thread 'main' panicked at 'Hello', main.rs:3

fn main() {
   let a = [10,20,30];
   a[10]; //invokes a panic since index 10 cannot be reached
}
Output is as shown below −
warning: this expression will panic at run-time
--> main.rs:4:4
  |
4 | a[10];
  | ^^^^^ index out of bounds: the len is 3 but the index is 10

$main
thread 'main' panicked at 'index out of bounds: the len 
is 3 but the index is 10', main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
A program can invoke the panic! macro if business rules are violated as shown in the example below −
fn main() {
   let no = 13; 
   //try with odd and even
   if no%2 == 0 {
      println!("Thank you , number is even");
   } else {
      panic!("NOT_AN_EVEN"); 
   }
   println!("End of main");
}
The above example returns an error if the value assigned to the variable is odd.
thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Enum Result – <T,E> can be used to handle recoverable errors. It has two variants − OK and Err. T and E are generic type parameters. T represents the type of the value that will be returned in a success case within the OK variant, and E represents the type of the error that will be returned in a failure case within the Err variant.
enum Result<T,E> {
   OK(T),
   Err(E)
}

Let us understand this with the help of an example −
use std::fs::File;
fn main() {
   let f = File::open("main.jpg"); 
   //this file does not exist
   println!("{:?}",f);
}
The program returns OK(File) if the file already exists and Err(Error) if the file is not found.
Err(Error { repr: Os { code: 2, message: "No such file or directory" } })

Let us now see how to handle the Err variant.
The following example handles an error returned while opening file using the match statement
use std::fs::File;
fn main() {
   let f = File::open("main.jpg");   // main.jpg doesn't exist
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
         println!("file not found \n{:?}",e);   //handled error
      }
   }
   println!("end of main");
}
NOTE − The program prints end of the main event though file was not found. This means the program has handled error gracefully.
file not found
Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
end of main

The is_even function returns an error if the number is not an even number. The main() function handles this error.
fn main(){
   let result = is_even(13);
   match result {
      Ok(d)=>{
         println!("no is even {}",d);
      },
      Err(msg)=>{
         println!("Error msg is {}",msg);
      }
   }
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}
NOTE − Since the main function handles error gracefully, the end of main statement is printed.
Error msg is NOT_AN_EVEN
end of main

The standard library contains a couple of helper methods that both enums − Result<T,E> and Option<T> implement. You can use them to simplify error cases where you really do not expect things to fail. In case of success from a method, the "unwrap" function is used to extract the actual result.
unwrap(self): T
Expects self to be Ok/Some and returns the value contained within. If it is Err or None instead, it raises a panic with the contents of the error displayed.
expect(self, msg: &str): T
Behaves like unwrap, except that it outputs a custom message before panicking in addition to the contents of the error.
The unwrap() function returns the actual result an operation succeeds. It returns a panic with a default error message if an operation fails. This function is a shorthand for match statement. This is shown in the example below −
fn main(){
   let result = is_even(10).unwrap();
   println!("result is {}",result);
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}
result is true
end of main
Modify the above code to pass an odd number to the is_even() function.
The unwrap() function will panic and return a default error message as shown below
thread 'main' panicked at 'called `Result::unwrap()` on 
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace

The program can return a custom error message in case of a panic. This is shown in the following example −
use std::fs::File;
fn main(){
   let f = File::open("pqr.txt").expect("File not able to open");
   //file does not exist
   println!("end of main");
}
The function expect() is similar to unwrap(). The only difference is that a custom error message can be displayed using expect.
thread 'main' panicked at 'File not able to open: Error { repr: Os 
{ code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Generics are a facility to write code for multiple contexts with different types. In Rust, generics refer to the parameterization of data types and traits. Generics allows to write more concise and clean code by reducing code duplication and providing type-safety. The concept of Generics can be applied to methods, functions, structures, enumerations, collections and traits.
The <T> syntax known as the type parameter, is used to declare a generic construct. T represents any data-type.
The following example declares a vector that can store only integers.
fn main(){
   let mut vector_integer: Vec<i32> = vec![20,30];
   vector_integer.push(40);
   println!("{:?}",vector_integer);
}
[20, 30, 40]

Consider the following snippet −
fn main() {
   let mut vector_integer: Vec<i32> = vec![20,30];
   vector_integer.push(40);
   vector_integer.push("hello"); 
   //error[E0308]: mismatched types
   println!("{:?}",vector_integer);
}
The above example shows that a vector of integer type can only store integer values. So, if we try to push a string value into the collection, the compiler will return an error. Generics make collections more type safe.
The type parameter represents a type, which the compiler will fill in later.
struct Data<T> {
   value:T,
}
fn main() {
   //generic type of i32
   let t:Data<i32> = Data{value:350};
   println!("value is :{} ",t.value);
   //generic type of String
   let t2:Data<String> = Data{value:"Tom".to_string()};
   println!("value is :{} ",t2.value);
}
The above example declares a generic structure named Data. The <T> type indicates some data type. The main() function creates two instances − an integer instance and a string instance, of the structure.
value is :350
value is :Tom

Traits can be used to implement a standard set of behaviors (methods) across multiple structures. Traits are like interfaces in Object-oriented Programming. The syntax of trait is as shown below −
trait some_trait {
   //abstract or method which is empty
   fn method1(&self);
   // this is already implemented , this is free
   fn method2(&self){
      //some contents of method2
   }
}
Traits can contain concrete methods (methods with body) or abstract methods (methods without a body). Use a concrete method if the method definition will be shared by all structures implementing the Trait. However, a structure can choose to override a function defined by the trait.
Use abstract methods if the method definition varies for the implementing structures.
impl some_trait for structure_name {
   // implement method1() there..
   fn method1(&self ){
   }
}

The following examples defines a trait Printable with a method print(), which is implemented by the structure book.
fn main(){
   //create an instance of the structure
   let b1 = Book {
      id:1001,
      name:"Rust in Action"
   };
   b1.print();
}
//declare a structure
struct Book {
   name:&'static str,
   id:u32
}
//declare a trait
trait Printable {
   fn print(&self);
}
//implement the trait
impl Printable for Book {
   fn print(&self){
      println!("Printing book with id:{} and name {}",self.id,self.name)
   }
}
Printing book with id:1001 and name Rust in Action

The example defines a generic function that displays a parameter passed to it. The parameter can be of any type. The parameter’s type should implement the Display trait so that its value can be printed by the println! macro.
use std::fmt::Display;

fn main(){
   print_pro(10 as u8);
   print_pro(20 as u16);
   print_pro("Hello TutorialsPoint");
}

fn print_pro<T:Display>(t:T){
   println!("Inside print_pro generic function:");
   println!("{}",t);
}
Inside print_pro generic function:
10
Inside print_pro generic function:
20
Inside print_pro generic function:
Hello TutorialsPoint

This chapter discusses how to accept values from the standard input (keyboard) and display values to the standard output (console). In this chapter, we will also discuss passing command line arguments.
Rust’s standard library features for input and output are organized around two traits −
Read
Write
Read
Types that implement Read have methods for byte-oriented input. They’re called readers
Write
Types that implement Write support both byte-oriented and UTF-8 text output. They’re called writers.
Readers are components that your program can read bytes from. Examples include reading input from the keyboard, files, etc. The read_line() method of this trait can be used to read data, one line at a time, from a file or standard input stream.
read_line(&mut line)->Result
Reads a line of text and appends it to line, which is a String. The return value is an io::Result, the number of bytes read.
Rust programs might have to accept values from the user at runtime. The following example reads values from the standard input (Keyboard) and prints it to the console.
fn main(){
   let mut line = String::new();
   println!("Enter your name :");
   let b1 = std::io::stdin().read_line(&mut line).unwrap();
   println!("Hello , {}", line);
   println!("no of bytes read , {}", b1);
}
The stdin() function returns a handle to the standard input stream of the current process, to which the read_line function can be applied. This function tries to read all the characters present in the input buffer when it encounters an end-of-line character.
Enter your name :
Mohtashim
Hello , Mohtashim
no of bytes read , 10

Writers are components that your program can write bytes to. Examples include printing values to the console, writing to files, etc. The write() method of this trait can be used to write data to a file or standard output stream.
write(&buf)->Result
Writes some of the bytes in the slice buf to the underlying stream. It returns an io::Result, the number of bytes written.
The print! or println! macros can be used to display text on the console. However, you can also use the write() standard library function to display some text to the standard output.
Let us consider an example to understand this.
use std::io::Write;
fn main() {
   let b1 = std::io::stdout().write("Tutorials ".as_bytes()).unwrap();
   let b2 = std::io::stdout().write(String::from("Point").as_bytes()).unwrap();
   std::io::stdout().write(format!("\nbytes written {}",(b1+b2)).as_bytes()).unwrap();
}
Tutorials Point
bytes written 15

The stdout() standard library function returns a handle to the standard output stream of the current process, to which the write function can be applied. The write() method returns an enum, Result. The unwrap() is a helper method to extract the actual result from the enumeration. The unwrap method will send panic if an error occurs.
NOTE − File IO is discussed in the next chapter.
CommandLine arguments are passed to a program before executing it. They are like parameters passed to functions. CommandLine parameters can be used to pass values to the main() function. The std::env::args() returns the commandline arguments.
The following example passes values as commandLine arguments to the main() function. The program is created in a file name main.rs.
//main.rs
fn main(){
   let cmd_line = std::env::args();
   println!("No of elements in arguments is :{}",cmd_line.len()); 
   //print total number of values passed
   for arg in cmd_line {
      println!("[{}]",arg); //print all values passed 
      as commandline arguments
   }
}
The program will generate a file main.exe once compiled. Multiple command line parameters should be separated by space. Execute main.exe from the terminal as main.exe hello tutorialspoint.
NOTE − hello and tutorialspoint are commandline arguments.
No of elements in arguments is :3
[main.exe]
[hello]
[tutorialspoint]

The output shows 3 arguments as the main.exe is the first argument.
The following program calculates the sum of values passed as commandline arguments. A list integer values separated by space is passed to program.
fn main(){
   let cmd_line = std::env::args();
   println!("No of elements in arguments is 
   :{}",cmd_line.len()); 
   // total number of elements passed

   let mut sum = 0;
   let mut has_read_first_arg = false;

   //iterate through all the arguments and calculate their sum

   for arg in cmd_line {
      if has_read_first_arg { //skip the first argument since it is the exe file name
         sum += arg.parse::<i32>().unwrap();
      }
      has_read_first_arg = true; 
      // set the flag to true to calculate sum for the subsequent arguments.
   }
   println!("sum is {}",sum);
}
On executing the program as main.exe 1 2 3 4, the output will be −
No of elements in arguments is :5
sum is 10

In addition to reading and writing to console, Rust allows reading and writing to files.
The File struct represents a file. It allows a program to perform read-write operations on a file. All methods in the File struct return a variant of the io::Result enumeration.
The commonly used methods of the File struct are listed in the table below −
Let us see an example to understand how to write a file.
The following program creates a file 'data.txt'. The create() method is used to create a file. The method returns a file handle if the file is created successfully. The last line write_all function will write bytes in newly created file. If any of the operations fail, the expect() function returns an error message.
use std::io::Write;
fn main() {
   let mut file = std::fs::File::create("data.txt").expect("create failed");
   file.write_all("Hello World".as_bytes()).expect("write failed");
   file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
   println!("data written to file" );
}
data written to file

The following program reads the contents in a file data.txt and prints it to the console. The "open" function is used to open an existing file. An absolute or relative path to the file is passed to the open() function as a parameter. The open() function throws an exception if the file does not exist, or if it is not accessible for whatever reason. If it succeeds, a file handle to such file is assigned to the "file" variable.
The "read_to_string" function of the "file" handle is used to read contents of that file into a string variable.
use std::io::Read;

fn main(){
   let mut file = std::fs::File::open("data.txt").unwrap();
   let mut contents = String::new();
   file.read_to_string(&mut contents).unwrap();
   print!("{}", contents);
}
Hello World
TutorialsPoint

The following example uses the remove_file() function to delete a file. The expect() function returns a custom message in case an error occurs.
use std::fs;
fn main() {
   fs::remove_file("data.txt").expect("could not remove file");
   println!("file is removed");
}
file is removed

The append() function writes data to the end of the file. This is shown in the example given below −
use std::fs::OpenOptions;
use std::io::Write;

fn main() {
   let mut file = OpenOptions::new().append(true).open("data.txt").expect(
      "cannot open file");
   file.write_all("Hello World".as_bytes()).expect("write failed");
   file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
   println!("file append success");
}
file append success

The following example copies the contents in a file to a new file.
use std::io::Read;
use std::io::Write;

fn main() {
   let mut command_line: std::env::Args = std::env::args();
   command_line.next().unwrap();
   // skip the executable file name
   // accept the source file
   let source = command_line.next().unwrap();
   // accept the destination file
   let destination = command_line.next().unwrap();
   let mut file_in = std::fs::File::open(source).unwrap();
   let mut file_out = std::fs::File::create(destination).unwrap();
   let mut buffer = [0u8; 4096];
   loop {
      let nbytes = file_in.read(&mut buffer).unwrap();
      file_out.write(&buffer[..nbytes]).unwrap();
      if nbytes < buffer.len() { break; }
   }
}
Execute the above program as main.exe data.txt datacopy.txt. Two command line arguments are passed while executing the file −
the path to the source file
the destination file
Cargo is the package manager for RUST. This acts like a tool and manages Rust projects.
Some commonly used cargo commands are listed in the table below −
cargo build
Compiles the current project.
cargo check
Analyzes the current project and report errors, but don't build object files.
cargo run
Builds and executes src/main.rs.
cargo clean
Removes the target directory.
cargo update
Updates dependencies listed in Cargo.lock.
cargo new
Creates a new cargo project.
Cargo helps to download third party libraries. Therefore, it acts like a package manager. You can also build your own libraries. Cargo is installed by default when you install Rust.
To create a new cargo project, we can use the commands given below.
cargo new project_name --bin

cargo new project_name --lib

To check the current version of cargo, execute the following command −
cargo --version

The game generates a random number and prompts the user to guess the number.
Open the terminal and type the following command cargo new guess-game-app --bin.
This will create the following folder structure.
guess-game-app/
   -->Cargo.toml
   -->src/
      main.rs

The cargo new command is used to create a crate. The --bin flag indicates that the crate being created is a binary crate. Public crates are stored in a central repository called crates.io https://crates.io/.
This example needs to generate a random number. Since the internal standard library does not provide random number generation logic, we need to look at external libraries or crates. Let us use rand crate which is available at crates.io website crates.io
The https://crates.io/crates/rand is a rust library for random number generation. Rand provides utilities to generate random numbers, to convert them to useful types and distributions, and some randomness-related algorithms.
The following diagram shows crate.io website and search result for rand crate.
Copy the version of rand crate to the Cargo.toml file rand = "0.5.5".
[package]
name = "guess-game-app"
version = "0.1.0"
authors = ["Mohtashim"]

[dependencies]
rand = "0.5.5"

Navigate to the project folder. Execute the command cargo build on the terminal window −
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.5.5
Downloading rand_core v0.2.2
Downloading winapi v0.3.6
Downloading rand_core v0.3.0
   Compiling winapi v0.3.6
   Compiling rand_core v0.3.0
   Compiling rand_core v0.2.2
   Compiling rand v0.5.5
   Compiling guess-game-app v0.1.0 
   (file:///E:/RustWorks/RustRepo/Code_Snippets/cargo-projects/guess-game-app)
   Finished dev [unoptimized + debuginfo] target(s) in 1m 07s

The rand crate and all transitive dependencies (inner dependencies of rand) will be automatically downloaded.
Let us now see how the business logic works for the number guessing game −
Game initially generates a random number.
Game initially generates a random number.
A user is asked to enter input and guess the number.
A user is asked to enter input and guess the number.
If number is less than the generated number, a message “Too low” is printed.
If number is less than the generated number, a message “Too low” is printed.
If number is greater than the generated number, a message “Too high” is printed.
If number is greater than the generated number, a message “Too high” is printed.
If the user enters the number generated by the program, the game exits.
If the user enters the number generated by the program, the game exits.
Add the business logic to main.rs file.
use std::io;
extern crate rand; 
//importing external crate
use rand::random;
fn get_guess() -> u8 {
   loop {
      println!("Input guess") ;
      let mut guess = String::new();
      io::stdin().read_line(&mut guess)
         .expect("could not read from stdin");
      match guess.trim().parse::<u8>(){ //remember to trim input to avoid enter spaces
         Ok(v) => return v,
         Err(e) => println!("could not understand input {}",e)
      }
   }
}
fn handle_guess(guess:u8,correct:u8)-> bool {
   if guess < correct {
      println!("Too low");
      false

   } else if guess> correct {
      println!("Too high");
      false
   } else {
      println!("You go it ..");
      true
   }
}
fn main() {
   println!("Welcome to no guessing game");

   let correct:u8 = random();
   println!("correct value is {}",correct);
   loop {
      let guess = get_guess();
      if handle_guess(guess,correct){
         break;
      }
   }
}
Execute the command cargo run on the terminal. Make sure that the terminal points to the Project directory.
Welcome to no guessing game
correct value is 97
Input guess
20
Too low
Input guess
100
Too high
Input guess
97
You got it ..

In this chapter, we will learn how iterators and closures work in RUST.
An iterator helps to iterate over a collection of values such as arrays, vectors, maps, etc. Iterators implement the Iterator trait that is defined in the Rust standard library. The iter() method returns an iterator object of the collection. Values in an iterator object are called items. The next() method of the iterator can be used to traverse through the items. The next() method returns a value None when it reaches the end of the collection.
The following example uses an iterator to read values from an array.
fn main() {
   //declare an array
   let a = [10,20,30];

   let mut iter = a.iter(); 
   // fetch an iterator object for the array
   println!("{:?}",iter);

   //fetch individual values from the iterator object
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
}
Iter([10, 20, 30])
Some(10)
Some(20)
Some(30)
None

If a collection like array or Vector implements Iterator trait then it can be traversed using the for...in syntax as shown below-
fn main() {
   let a = [10,20,30];
   let iter = a.iter();
   for data in iter{
      print!("{}\t",data);
   }
}

10 20 30

The following 3 methods return an iterator object from a collection, where T represents the elements in a collection.
iter()
gives an iterator over &T(reference to T)
into_iter()
gives an iterator over T
iter_mut()
gives an iterator over &mut T
The iter() function uses the concept of borrowing. It returns a reference to each element of the collection, leaving the collection untouched and available for reuse after the loop.
fn main() {
   let names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.iter() {
      match name {
         &"Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   println!("{:?}",names); 
   // reusing the collection after iteration
}
Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

This function uses the concept of ownership. It moves values in the collection into an iter object, i.e., the collection is consumed and it is no longer available for reuse.
fn main(){
   let names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.into_iter() {
      match name {
         "Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   // cannot reuse the collection after iteration
   //println!("{:?}",names); 
   //Error:Cannot access after ownership move
}
Hello Kannan
There is a rustacean among us!
Hello Kiran

This function is like the iter() function. However, this function can modify elements within the collection.
fn main() {
   let mut names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.iter_mut() {
      match name {
         &mut "Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   println!("{:?}",names);
   //// reusing the collection after iteration
}
Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

Closure refers to a function within another function. These are anonymous functions – functions without a name. Closure can be used to assign a function to a variable. This allows a program to pass a function as a parameter to other functions. Closure is also known as an inline function. Variables in the outer function can be accessed by inline functions.
A closure definition may optionally have parameters. Parameters are enclosed within two vertical bars.
let closure_function = |parameter| {
   //logic
}

The syntax invoking a Closure implements Fn traits. So, it can be invoked with () syntax.
closure_function(parameter);    //invoking

The following example defines a closure is_even within the function main(). The closure returns true if a number is even and returns false if the number is odd.
fn main(){
   let is_even = |x| {
      x%2==0
   };
   let no = 13;
   println!("{} is even ? {}",no,is_even(no));
}
13 is even ? false

fn main(){
   let val = 10; 
   // declared outside
   let closure2 = |x| {
      x + val //inner function accessing outer fn variable
   };
   println!("{}",closure2(2));
}
The main() function declares a variable val and a closure. The closure accesses the variable declared in the outer function main().
12

Rust allocates everything on the stack by default. You can store things on the heap by wrapping them in smart pointers like Box. Types like Vec and String implicitly help heap allocation. Smart pointers implement traits listed in the table below. These traits of the smart pointers differentiate them from an ordinary struct −
std::ops::Deref
Used for immutable dereferencing operations, like *v.
std::ops::Drop
Used to run some code when a value goes out of scope. This is sometimes called a destructor
In this chapter, we will learn about the Box smart pointer. We will also learn how to create a custom smart pointer like Box.
The Box smart pointer also called a box allows you to store data on the heap rather than the stack. The stack contains the pointer to the heap data. A Box does not have performance overhead, other than storing their data on the heap.
Let us see how to use a box to store an i32 value on the heap.
fn main() {
   let var_i32 = 5; 
   //stack
   let b = Box::new(var_i32); 
   //heap
   println!("b = {}", b);
}
b = 5

In order to access a value pointed by a variable, use dereferencing. The * is used as a dereference operator. Let us see how to use dereference with Box.
fn main() {
   let x = 5; 
   //value type variable
   let y = Box::new(x); 
   //y points to a new value 5 in the heap

   println!("{}",5==x);
   println!("{}",5==*y); 
   //dereferencing y
}
The variable x is a value-type with the value 5. So, the expression 5==x will return true. Variable y points to the heap. To access the value in heap, we need to dereference using *y. *y returns value 5. So, the expression 5==*y returns true.
true
true

The Deref trait, provided by the standard library, requires us to implement one method named deref, that borrows self and returns a reference to the inner data. The following example creates a structure MyBox, which is a generic type. It implements the trait Deref. This trait helps us access heap values wrapped by y using *y.
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> { 
   // Generic structure with static method new
   fn new(x:T)-> MyBox<T> {
      MyBox(x)
   }
}
impl<T> Deref for MyBox<T> {
   type Target = T;
   fn deref(&self) -> &T {
      &self.0 //returns data
   }
}
fn main() {
   let x = 5;
   let y = MyBox::new(x); 
   // calling static method
   
   println!("5==x is {}",5==x);
   println!("5==*y is {}",5==*y); 
   // dereferencing y
   println!("x==*y is {}",x==*y);
   //dereferencing y
}
5==x is true
5==*y is true
x==*y is true

The Drop trait contains the drop() method. This method is called when a structure that implemented this trait goes out of scope. In some languages, the programmer must call code to free memory or resources every time they finish using an instance of a smart pointer. In Rust, you can achieve automatic memory deallocation using Drop trait.
use std::ops::Deref;

struct MyBox<T>(T);
impl<T> MyBox<T> {
   fn new(x:T)->MyBox<T>{
      MyBox(x)
   }
}
impl<T> Deref for MyBox<T> {
   type Target = T;
      fn deref(&self) -< &T {
      &self.0
   }
}
impl<T> Drop for MyBox<T>{
   fn drop(&mut self){
      println!("dropping MyBox object from memory ");
   }
}
fn main() {
   let x = 50;
   MyBox::new(x);
   MyBox::new("Hello");
}
In the above example, the drop method will be called twice as we are creating two objects in the heap.
dropping MyBox object from memory
dropping MyBox object from memory

In Concurrent programming, different parts of a program execute independently. On the other hand, in parallel programming, different parts of a program execute at the same time. Both the models are equally important as more computers take advantage of their multiple processors.
We can use threads to run codes simultaneously. In current operating systems, an executed program’s code is run in a process, and the operating system manages multiple processes at once. Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads.
The thread::spawn function is used to create a new thread. The spawn function takes a closure as parameter. The closure defines code that should be executed by the thread. The following example prints some text from a main thread and other text from a new thread.
//import the necessary modules
use std::thread;
use std::time::Duration;

fn main() {
   //create a new thread
   thread::spawn(|| {
      for i in 1..10 {
         println!("hi number {} from the spawned thread!", i);
         thread::sleep(Duration::from_millis(1));
      }
   });
   //code executed by the main thread
   for i in 1..5 {
      println!("hi number {} from the main thread!", i);
      thread::sleep(Duration::from_millis(1));
   }
}
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!

The main thread prints values from 1 to 4.
NOTE − The new thread will be stopped when the main thread ends. The output from this program might be a little different every time.
The thread::sleep function forces a thread to stop its execution for a short duration, allowing a different thread to run. The threads will probably take turns, but that is not guaranteed – it depends on how the operating system schedules the threads. In this run, the main thread is printed first, even though the print statement from the spawned thread appears first in the code. Moreover, even if the spawned thread is programmed to print values till 9, it only got to 5 before the main thread shut down.
A spawned thread may not get a chance to run or run completely. This is because the main thread completes quickly. The function spawn<F, T>(f: F) -> JoinHandlelt;T> returns a JoinHandle. The join() method on JoinHandle waits for the associated thread to finish.
use std::thread;
use std::time::Duration;

fn main() {
   let handle = thread::spawn(|| {
      for i in 1..10 {
         println!("hi number {} from the spawned thread!", i);
         thread::sleep(Duration::from_millis(1));
      }
   });
   for i in 1..5 {
      println!("hi number {} from the main thread!", i);
      thread::sleep(Duration::from_millis(1));
   }
   handle.join().unwrap();
}
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 2 from the main thread!
hi number 3 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!

The main thread and spawned thread continue switching.
NOTE − The main thread waits for spawned thread to complete because of the call to the join() method.

    45 Lectures 
     4.5 hours 

 Stone River ELearning

    10 Lectures 
     33 mins

 Ken Burke
 Print
 Add Notes
 Bookmark this page