In this session, we will delve into the fundamental concepts of rust, why Rust is most beloved language over the years, and also discuss about some key features of rust like ownership and borrowing that makes is suitable for high performance systems.
2. Lack of etiquette and manners is a huge turn off.
KnolX Etiquettes
Punctuality
Join the session 5 minutes prior to the session start time. We start on
time and conclude on time!
Feedback
Make sure to submit a constructive feedback for all sessions as it is very
helpful for the presenter.
Silent Mode
Keep your mobile devices in silent mode, feel free to move out of session
in case you need to attend an urgent call.
Avoid Disturbance
Avoid unwanted chit chat during the session.
3. 1. Introduction to Rust
Brief history and motivation behind the
creation of Rust
Key features.
2. Setting Up Rust
Installation of Rust programming language.
Overview of Rust toolchains and package
manager(Cargo).
3. Writing First Program In Rust
4. Ownership and Borrowing
Understanding Rust's Ownership system.
5. Data Types and Variable
6. Control Flow
7. Functions
4.
5. Brief History
Rust is a System programming language that was first announced by Mozilla in 2010 and later
officially released in 2015 .
The language was created by a team led by Mozilla employee Graydon Hoare.
Rust was developed as a response to the challenges and limitations of existing programming
language , particularly in the context of system-level programming .
Before Rust ,C and C++ were dominant choices for systems programming due to their
performance and low-level control.
However , these languages were also notorious for memory safety issues like buffer overflows
and dangling pointers , leading to vulnerabilities and bugs that could be difficult to debug and fix .
Rust aimed to address these issues by providing a
safer alternative without sacrificing performance.
6. Motivation
The primary motivation behind Rust's creation was to provide a programming language that offers
the performance of low-level language like C and C++ while eliminating common programming
errors related to memory safety.
Rust was designed to be a system programming language that is memory-safe ,concurrent ,and
practical .
The goal was to empower developers to write reliable and efficient code without compromising on
performance or sacrificing control over system resources.
7. Key features
Memory Safety : One of Rust 's standout feature is its ownership system, which enforces strict
rules on how memory is accessed and modified. This system eliminates common programming
errors like null pointers dereference and data races ,leading to more reliable and secure software.
Zero-Cost Abstraction : Rust provides high-level abstraction without incurring runtime overhead.
This means developers can use features like pattern matching, generics , and trait-
based polymorphism without sacrificing performance. The abstractions are designed to be as
efficient as hand-written ,low-level code.
Concurrency Without Data Races : Rust ownership and borrowing system ensures that
concurrent access to data is safe , preventing data races at compile-time. This allows developer
to write concurrent code with confidence that it will free of certain classes of bugs common in
parallel programming.
Borrow Checker : Rust 's borrow checker is a key component of its ownership system ,statically
analysing code to ensure that reference to memory are valid at compile-time. While it can
be strict , it prevents common bugs associated with manual memory management .
8. Setting Up Rust
To get started with rust you need to install Rust programming language in your system.
Rust comes with a comprehensive toolchain and a package manager called Cargo.
• Installation of the Rust programming language :
• Go to the official website of Rust at : https://www.rust-lang.org/
• Download and run the installer :
• The website will typically detect your operating system and provides you with the appropriate installer
• Customized Installation(Optional) :
• During the installation , you have the option to customize the installation settings . This includes selecting
components to install , such as documentation , source code and additional tools.
• Check installation :
• Open a new terminal or command prompt and type the following command to verify that Rust is installed :
rustc --version
9. Overview of Rust toolchain
Rust comes with a set of tools that make up its toolchain . The key components include :
rustc(Rust Compiler) : rustc is the Rust compiler ,responsible for translating Rust source code into
machine code that can be executed by the computer .
Cargo : 'Cargo' is Rust's package manager and build system . It simplifies the process of
managing dependency , building projects , and running tests .Cargo is an essential tool for Rust
development .
rustup : rustup is the Rust toolchain installer . It allows you to easily manage multiple version of Rust,
switch between them , and keep your toolchain up to date .
Package manager -Cargo:
Cargo is an integral part of Rust ecosystem, and it serves several purpose:
Dependency management :Cargo manages project dependency by specifying them in 'Cargo.toml'
file .It automatically fetches and build the required dependency for your project .
Building project : Cargo simplifies the process of building Rust projects .You can use 'cargo build ' to
compile your project and 'cargo run ' to build and run it.
10. Testing: Cargo provides built-in support for writing and running tests, The 'cargo test' commands
allows you to execute tests in your project.
Documentation: Cargo integrates with Rust's Documentation system .You can use 'cargo doc' to
generate and view documentation for your project and its dependencies.
Creating projects :Cargo makes it easy to start new projects with the 'cargo new' commands.
This command sets up a new Rust project with a basic directory structure and a
default configuration.
To create a new project :
cargo new project_name
This will create a basic Rust project with the necessary file and you can start adding your code
to the 'src' directory .
11. Writing first programme in Rust
// This is a simple "Hello, World!" program in Rust.
fn main() {
// The `println!` macro is used to print text to the console.
println!("Hello, World!");
}
Explanation :
Comments :
In Rust comments are created using '//' for single-line comment or '/* */' for multiline comments .
Comments are optional and are ignored by the Rust compiler.
Function Definition:
The 'fn' keyword is used to define a function.
The 'main' function is a special function in Rust that serves as the entry for the executable
programs .
The parenthesis '()' denote that 'main' function takes no parameter .
12. • Code Block:
• The code block , enclosed by curly braces '{}', contains the body of the 'main' function.
• Print Statement :
• The 'println!' Macro is used for formatted printing to the console .
• The exclamation mark ('!') indicates that it's a macro invocation .
• The content within the double quotes is the text to be printed .
• Rust program typically have the following structure :
// Comments at the beginning can provide information about the program.
// Import external crates or modules if needed.
// Define any constants or global variables.
// Define functions and their implementations.
// The `main` function, serving as the entry point for the program.
fn main() {
// Code within the main function.
}
13. Ownership and borrowing
Rust's ownership system is a fundamental and unique aspect of the language designed to ensure
memory safety without the need for garbage collection .The ownership system is based on three
key concepts :ownership , borrowing , and lifetimes.
Ownership :
o In Rust, each value has a variable that is its "owner". The owner is responsible for cleaning up the value
's memory when it's no longer needed . Ownership is transferable, meaning ownership of a value can be
moved from one variable to another .
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // Ownership of the String is moved from s1 to s2.
// println!("{}", s1); // This line would result in an error, as s1 no longer owns the String.
println!("{}", s2); // This is valid, as s2 now owns the String.
}
o In this example 's1' initially owns the 'String', but when it is assigned to 's2', ownership is moved , and
attempting to use 's1' afterward would result in a compilation error.
14. o Borrowing : Instead of transferring ownership , Rust allows borrowing , where a reference to a value is
passed to another part of the code . Borrowing comes in two forms : mutable borrowing and immutable
borrowing
Immutable borrowing : Multiple parts of the code can have read-only access to the same data
simultaneously .
fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s1 = String::from("Hello");
let len = calculate_length(&s1);
println!("Length of '{}' is {}.", s1, len);
}
Here, 'calculate_length' takes an immutable referance ('&String') to 's1', allowing it to read the content of
the 'String' without taking ownership.
15. Mutable Borrowing : Only one part of the code can have write access to the data at a time , preventing data
races .
fn change_string(s: &mut String) {
s.push_str(", Rust!");
}
fn main() {
let mut s1 = String::from("Hello");
change_string(&mut s1);
println!("{}", s1); // This is valid, as s1 is borrowed mutably.
}
The '&mut String' reference in 'change_string' allows mutable borrowing ,enabling the function to modify
the content of the 'String'
Ownership Rules : To ensure memory safety , Rust enforces a set of ownership rules.
Each value has a single owner at any given time .
Value are freed when the owner goes out of scope .
Reference must follow the borrowing rules (either one mutable reference any number of immutable
reference , but not both simultaneously ).
16. Data Types
In Rust, a variable is a symbolic name for a memory location that stores data. Unlike some
languages, variables in Rust are immutable by default. Once a value is assigned, it cannot be
changed.
let x = 5;
Here, x is immutable, and any attempt to modify it will result in a compile-time error.
To make a variable mutable, the mut keyword is used. This allows for changes to the value stored
in the variable
let mut y = 10;
y = y + 1;
The mut keyword signifies that y is mutable, and it can be modified during its scope.
Rust is a statically typed language, which means that it must know the types of all variables
at compile time. The compiler can usually infer what type we want to use based on the value
and how we use it. There are two data type subsets: scalar and compound.
17. Scalar Types - A scalar type represents a single value. Rust has four primary scalar
types: integers, floating-point numbers, Booleans, and characters.
Integer Types -
An integer is a number without a fractional component.
Each variant can be either signed or unsigned and has an explicit size. Signed and unsigned
refer to whether it’s possible for the number to be negative—in other words, whether the number
needs to have a sign with it (signed) or whether it will only ever be positive and can therefore be
represented without a sign (unsigned).
let a: i32 = -42;
let b: u64 = 42;
Here, a is a signed 32-bit integer, and b is an
unsigned 64-bit integer.
18. Floating-Point Types -
Rust also has two primitive types for floating-point numbers, which are numbers with
decimal points. Rust’s floating-point types are f32 and f64, which are 32 bits and 64 bits in
size, respectively. The default type is f64 because on modern CPUs, it’s roughly the
same speed as f32 but is capable of more precision. All floating-point types are signed.
let x = 2.0; // f64
let y: f32 = 3.0; // f32
Booleans Type -
As in most other programming languages, a Boolean type in Rust has two possible values:
true and false. Booleans are one byte in size. The Boolean type in Rust is specified using bool.
let t = true;
let f: bool = false; // with explicit type annotation
Character Type -
The char type in Rust represents a Unicode character.
let my_char: char = 'A';
19. Compound Types - A Compound type can group multiple values into one type. Rust has two
primitive compound types: tuples and arrays.
Tuples -
A tuple is a general way of grouping together a number of values with a variety of types into one
compound type. Tuples have a fixed length: once declared, they cannot grow or shrink in size. The
empty tuple() is also known as the "Unit Type".
We create a tuple by writing a comma-separated list of values inside parentheses. Each position in
the tuple has a type, and the types of the different values in the tuple don’t have to be the same.
Example -
let tup: (i32, f64, u8) = (500, 6.4, 1);
The variable tup binds to the entire tuple because a tuple is considered a single compound element. To
get the individual values out of a tuple, we can use pattern matching to destructure a tuple value.
let (x, y, z) = tup;
This is called destructuring because it breaks the single tuple into three parts. We can also access a
tuple element directly by using a period (.) followed by the index of the value we want to access.
20. Arrays -
Another way to have a collection of multiple values is with an array. Unlike a tuple, every element of an array
must have the same type. Unlike arrays in some other languages, arrays in Rust have a fixed length. Arrays
are useful when you want your data allocated on the stack rather than the heap or when you want to ensure
you always have a fixed number of elements.
We write an array’s type using square brackets with the type of each element, a semicolon, and then the
number of elements in the array, like so:
let a: [i32; 5] = [1, 2, 3, 4, 5];
You can also initialize an array to contain the same value for each element by specifying the initial value,
followed by a semicolon, and then the length of the array in square brackets, as shown here:
let a = [3; 5];
This is the same as writing let a = [3, 3, 3, 3, 3]; but in a more concise way. You can access elements of an
array using indexing, like this:
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
21. Control Flow
The ability to run some code depending on whether a condition is true and to run some code
repeatedly while a condition is true are basic building blocks in most programming languages. The
most common constructs that let you control the flow of execution of Rust code are if expressions
and loops.
If Expressions -
An if expression allows you to branch your code depending on conditions. You provide a condition
and then state, “If this condition is met, run this block of code. If the condition is not met, do not run
this block of code.”
22. Repetition with Loops -
It’s often useful to execute a block of code more than once. For this task, Rust provides several
loops, which will run through the code inside the loop body to the end and then start immediately
back at the beginning. Rust has three kinds of loops: loop, while, and for.
Loop -
The loop keyword tells Rust to execute a block of code over and over again forever or until you
explicitly tell it to stop.
23. While Loop -
A program will often need to evaluate a condition within a loop. While the condition is true,
the loop runs. When the condition evaluates to false, the program calls break, stopping
the loop.
This construct eliminates a lot of nesting that would be necessary if you used loop, if,
else, and break, and it’s clearer. While a condition evaluates to true, the code runs;
otherwise, it exits the loop.
24. For Loop -
The for loop specifically is designed for iterating over a sequence of elements, such as arrays,
ranges, or other iterable structures.
An example to use a Range, provided by the standard library, which generates all numbers in
sequence starting from one number and ending before another number.
fn main() {
for number in 1..10 {
print!("{number} ");
}
println!("Range Over!!!");
}
25. Functions -
Rust code uses snake case as the conventional style for function and variable names, in which all
letters are lowercase and underscores separate words.
We define a function in Rust by entering fn followed by a function name and a set of
parentheses. The curly brackets tell the compiler where the function body begins and ends.
26. Parameters -
We can define functions to have parameters, which are special variables that are part of
a function’s signature. When a function has parameters, you can provide it with
concrete values for those parameters. Technically, the concrete values are called
arguments.
In function signatures, you must declare the type of each parameter.
When defining multiple parameters, separate the parameter declarations with commas
27. Function with Return Values -
Functions can return values to the code that calls them. We don’t name return values, but we
must declare their type after an arrow (->). In Rust, the return value of the function is synonymous
with the value of the final expression in the block of the body of a function. You can return early
from a function by using the return keyword and specifying a value, but most functions return the
last expression implicitly.