Rust Programming
Rust Error Troubleshooting Comprehensive Guide to Memory Management
- Error Handling In Rust
- Rust unwrap() and expect() In Rust
- Ownership In Rust
- References and Borrowing In Rust
¶Error Handling In Rust
An error is an unrehearsed behavior or event in a program that will generate an unwanted output.
In Rust Program, errors are two categories:
- Unrecoverable Errors
- Recoverable Errors
¶Unrecoverable Errors in Rust Programming
Unrecoverable errors are errors to which a program stops its performance. As the name suspects, we cannot restore from unrecoverable errors.
this errors are known as panic and can be triggered apparently by calling the panic!
macro.
Now let’s look at an example that uses the panic!
macro.
¶Example 1: Unrecoverable Errors with panic! Macro In Rust
fn main() {
println!("Hello, World!");
// Explicitly exit the program with an unrecoverable error
panic!("Crash");
}
Output :
Hello, World!
thread 'main' panicked at 'Crash', src/main.rs:5:5
Hither, the call to the panic!
macro reasons an unrecoverable error.
thread 'main' panicked at 'Crash', src/main.rs:5:5
Follow that the program still runs the evolutions above panic!
macro. We can only see Hello, World!
printed to the system at first the error message
The panic!
macro receives in an error message as an argument.
¶Example 2: Unrecoverable Errors In Rust
Unrecoverable errors are further triggered by receiving an action that might cause our code to panic. For example- appreciating an array past its index will cause a panic.
fn main() {
let numbers = [1, 2 ,3];
println!("unknown index value = {}", numbers[3]);
}
Error
error: this operation will panic at runtime
--> src/main.rs:4:42
|
4 | println!("unknown index value = {}", numbers[3]);
| ^^^^^^^^^^ index out of bounds: the length is 3 but the index is 3
|
Rust program stops us from making the program because it knows the activities will panic at runtime
The layout numbers
does not have a value at index 3 i.e. numbers[3]
.
¶Recoverable Errors In Rust
Recoverable errors are errors that won’t stop a program from acting. Most errors are recoverable and we do easily receive action based on the type of error.
For example-if you try to open a file which doesn’t exist, you can make the file instead of stopping the performance of the program or departing the program with a panic.
Now see at an example-
use std::fs::File;
fn main() {
let data_result = File::open("data.txt");
// using match for Result type
let data_file = match data_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the data file: {:?}", error),
};
println!("Data file", data_file);
}
If the data.txt
file subsist, the output is here:
Data file: File { fd: 3, path: "/playground/data.txt", read: true, write: false }
If the data.txt
file doesn’t subsist, the output is here:
thread 'main' panicked at 'Problem opening the data file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:8:23
¶The Result Enum In Rust
In the upon example, return type of the File::open('data.txt')
is a Result<T, E>
.
The Result<T, E>
type returns other a value or an error in Rust Program. It’s an enum
type with two probable variants.
-
Ok(T)
→ operation repeated with valueT
- Err(E) → operation unsuccessful with an error
E
The most primary way to see if a Result enum
has a value or error is to use pattern become like with a match
evolution.
// data_file is a Result<T, E>
match data_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the data file: {:?}", error),
};
Whereas the result is Ok
, these code will return the file
, and Whereas the result is Err
, these will return a panic!
.
¶The Option Enum In Rust
The Option
type or Option<T>
type is an enum
type as like the Result
with two probable variants.
* `None` → to reveal failure with no value
* `Some(T)` → a value type `T`
Now see an example-
fn main() {
let text = "Hello, World!";
let character_option = text.chars().nth(15);
// using match for Option type
let character = match character_option {
None => "empty".to_string(),
Some(c) => c.to_string()
};
println!("Character at index 15 is {}", character);
}
Output :
Character at index 15 is empty
The method text.chars().nth(15)
interchange an Option<String>
. Now, to be the value out of the Option
, you use a match
evolution.
The example upon, the 15th index of the string text
doesn’t subsist. these, the Option
type returns a None
as matches the "empty"
string.
None => "empty".to_string()
If you were to be the 11th index of the string text
, the Option
enum will return Some(c)
, Here c
is the letter in the 11th index.
Let’s update the upon example to discovery out the 11th index in the string.
fn main() {
let text = "Hello, World!";
let character_option = text.chars().nth(11);
// using match for Option type
let character = match character_option {
None => "empty".to_string(),
Some(c) => c.to_string()
};
println!("Character at index 11 is {}", character);
}
Output :
Character at index 11 is d
¶Rust unwrap() and expect() In Rust
The unwrap()
and expect()
appropriateness methods that work with Option and Result types in Rust Program.
¶The unwrap() Method In Rust
Unwrap in Rust Program returns the consequence of the operation for Option
and Result
enums. If unwrap appointments an error Err
or None
, it’s will panic and stop the program performance.
Unwrap method are defined on both Option
and Result
type.
An Option
enum type can be moved about by using the match
evolution as well as unwrap()
.
¶Example: Using the match Expression In Rust
// function to find a user by their username which returns an Option type
fn get_user(username: &str) -> Option<&str> {
if username.is_empty() {
return None;
}
return Some(username);
}
fn main() {
// returns an Option
let user_option = get_user("Hari");
// use of match expression to get the result out of Option
let result = match user_option {
Some(user) => user,
None => "not found!",
};
// print the result
println!("user = {:?}", result);
}
Output :
user = "Hari"
Now, you have a get_user
function that reports an Option
type. It can other return Some(&str)
or None
.
Here, it’s program can use the unwrap()
method to be release of the match
evolution which is a little wordy.
Now we use unwrap()
in the upon example.
¶Example: Using unwrap() In Rust
// function to find a user by their username which return an Option enum
fn get_user(username: &str) -> Option<&str> {
if username.is_empty() {
return None;
}
return Some(username);
}
fn main() {
// use of unwrap method to get the result of Option enum from get_user function
let result = get_user("Hari").unwrap();
// print the result
println!("user = {:?}", result);
}
Output :
user = "Hari"
Both the match
evolution and unwrap()
gives us the equivalent output. The only inequality existence that unwrap()
will panic if the return value is a None
.
¶The expect() Method In Rust
expect()
is very like to unwrap()
with the collation of a custom panic message as an contention.
The expect()
method is defined on both Option
and Result
type.
Now update the upon example to use expect()
instead of unwrap()
.
// function to find a user by their username which return an Option enum
fn get_user(username: &str) -> Option<&str> {
if username.is_empty() {
return None;
}
return Some(username);
}
fn main() {
// use of expect method to get the result of Option enum from get_user function
let result = get_user("").expect("fetch user");
// print the result
println!("user = {:?}", result);
}
Output :
thread 'main' panicked at 'fetch user', src/main.rs:12:31
Below, we use the expect()
with a panic message as the contention.
expect()
and unwrap()
will generate the same result if there’s no probability of Option
returning None
and Result
returning Err
.
The Question Mark (?) Operator In Rust
The question mark (?)
operator is a shorthand for revolving the Result
. It’s can only be practical to Result<T, E>
and Option<T>
type.
Now we apply ?
to Result<T, E>
type:
- Supposing the value is
Err(e)
, it returns anErr()
instantly - Supposing the value is
Ok(x)
, it unwraps and returnsx
Now look at an example-
use std::num::ParseIntError;
// Function to parse an integer
fn parse_int() -> Result<i32, ParseIntError> {
// Example of ? where value is unwrapped
let x: i32 = "12".parse()?; // x = 12
// Example of ? where error is returned
let y: i32 = "12a".parse()?; // returns an Err() immediately
Ok(x + y) // Doesn't reach this line
}
fn main() {
let res = parse_int();
println!("{:?}", res);
}
Output :
Err(ParseIntError { kind: InvalidDigit })
The way, error stirring in the function is attenuate to a single line of code, building it cleaner and simple to read.
¶Ownership In Rust
Rust Program comprise an ownership appointments to manage the memory of our program. proprietary is a set of rules that confirm memory safety in Rust programs.
¶Variable Scope in Rust Program
A scope is a code block among the program for as a variable is valid. The scope of a variable identify its ownership.
For Example-
// `name` is invalid and cannot be used here because it's not yet declared
{ // code block starts here
let name = String::from("Ram Nepali"); // `name` is valid from this point forward
// do stuff with `name`
} // code block ends
// this scope ends, `name` is no longer valid and cannot be used
Above the variable name
is only obtainable inside the code block, i.e., among the curly braces {}
. You cannot use the name
variable beside the closing curly brace.
Ownership Rules in Rust Program
Rust Program has some ownership rules. placement these rules in mind as we work by some examples-
- Every value in Rust Program has an owner.
- This can only be one owner contemporary.
- When the owner passage out of scope, the value will be exuded.
¶Data Move in Rust program
Once in a way, we strength not want a variable to be exuded at the end of the scope. Instead, we want to disposal ownership of an item from one binding (variable) to different.
Nows an example to understand data stroll and ownership rules in Rust Program.
fn main() {
// owner of the String value
// rule no. 1
let fruit1 = String::from("Banana");
// ownership moves to another variable
// only one owner at a time
// rule no. 2
let fruit2 = fruit1;
// cannot print variable fruit1 because ownership has moved
// error, out of scope, value is dropped
// rule no. 3
// println!("fruit1 = {}", fruit1);
// print value of fruit2 on the screen
println!("fruit2 = {}", fruit2);
}
Output :
fruit2 = Banana
Now, look into this example in detail, particularly these two lines of code
let fruit1 = String::from("Banana");
let fruit2 = fruit1;
A String
store data both of the stack and the heap. That’s mean that when we bind a String
to a variable fruit1
, the memory deputation looks like this below-
A String
maintain a pointer to the memory that maintain the content of the string, a length, and a receptivity in the stack. The rick on the right hand side of the graph holds the contents of the String
.
When we assign fruit1
to fruit2
, this is how the memory deputation looks like-
¶Copy Data in Rust program
primordial types like Integers, Floats and Booleans don’t ensue the ownership rules. this types have a known size at compose time and are stored absolutely on the stack, so cabbage of the authentic values are quick to make. For example-
fn main() {
let x = 11;
// copies data from x to y
// ownership rules are not applied here
let y = x;
println!("x = {}, y = {}", x, y);
}
Output :
x = 11, y = 11
Below, x
variable can be used after, unlike a move without disquieting about ownership, even though y
is imposed to x
.
¶Functions Ownership in Rust
Momentary a variable to a function will step or copy, just as an employment. Stack-only types will copy the data when acquired into a function. stack data types will move the ownership of the variable to the function.
1. Passing String to a function In Rust
fn main() {
let fruit = String::from("Apple"); // fruit comes into scope
// ownership of fruit moves into the function
print_fruit(fruit);
// fruit is moved to the function so is no longer available here
// error
// println!("fruit = {}", fruit);
}
fn print_fruit(str: String) { // str comes into scope
println!("str = {}", str);
} // str goes out of scope and is dropped, plus memory is freed
Output :
str = Apple
The value of the fruit
variable is driven into the function print_fruit()
forasmuch as String
type uses stack memory
2. Passing Integer to a function In Rust
fn main() {
// number comes into scope
let number = 10;
// value of the number is copied into the function
print_number(number);
// number variable can be used here
println!("number = {}", number);
}
fn print_number(value: i32) { // value comes into scope
println!("value = {}", value);
} // value goes out of scope
Output :
value = 10
number = 10
The value of the number
variable is followed among the function print_number()
because the i32
(integer) type exercise stack memory
¶References and Borrowing In Rust
References in Rust programs assume us to point to a resource (value) without identify it. That means the original owner of the resource remnant the same.
References are subsidiary when passing values to a function that we do not want to alternative the ownership of. Making a reference is known as borrowing in Rust.
¶Understanding References in Rust Program
Now look an example to learn about references in Rust Program
fn main() {
let str = String::from("Hello, World!");
// Call function with reference String value
let len = calculate_length(&str);
println!("The length of '{}' is {}.", str, len);
}
// Function to calculate length of a string
// It takes a reference of a String as an argument
fn calculate_length(s: &String) -> usize {
s.len()
}
Output :
The length of 'Hello, World!' is 13.
In the upon example, we identify a function called calculate_length()
which accepts a &String
type as an contention.
The significant part here is that s
is a reference to a String
and it doesn’t accept ownership of the original value of String
.
fn calculate_length(s: &String) -> usize { // s is a reference to a String
s.len()
}
The function call looks like as:
let str = String::from("Hello, World!");
let len = calculate_length(&str);
The &str
syntax when calling the function lets us make a reference that mention to the value of str
but does not own it.
¶Modifying a Reference in Rust Program
By inability a reference is constantly immutable. Only, we can conduct the &mut
keyword to create a reference mutable.
Now, For Example-
fn main() {
let mut str = String::from("Hello");
// before modifying the string
println!("Before: str = {}", str);
// pass a mutable string when calling the function
change(&mut str);
// after modifying the string
println!("After: str = {}", str);
}
fn change(s: &mut String) {
// push a string to the mutable reference variable
s.push_str(", World!");
}
Output :
Before: str = Hello
After: str = Hello, World!
You set the variable str
to be mutable. After you create a mutable mention with &mut str
, and call the change()
function with a mutable mention s: &mut String
.
This make allowance for the change()
function to modify the value it sharpen. Inside the change()
function, we push a string with s.push_str(“, World!“) to the reference string.
Exploring the Core Features of Rust Standard Library
Rust Modules: A Comprehensive Guide to Packages and Best Practices
All Tutorials in this playlist
Popular Tutorials
Categories
-
Artificial Intelligence (AI)
11
-
Bash Scripting
1
-
Bootstrap CSS
0
-
C Programming
14
-
C#
0
-
ChatGPT
1
-
Code Editor
2
-
Computer Engineering
3
-
CSS
28
-
Data Structure and Algorithm
18
-
Design Pattern in PHP
2
-
Design Patterns - Clean Code
1
-
E-Book
1
-
Git Commands
1
-
HTML
19
-
Interview Prepration
2
-
Java Programming
0
-
JavaScript
12
-
Laravel PHP Framework
37
-
Mysql
1
-
Node JS
1
-
Online Business
0
-
PHP
28
-
Programming
8
-
Python
12
-
React Js
19
-
React Native
1
-
Redux
2
-
Rust Programming
15
-
SEO - Search Engine Optimization
1
-
Tailwind CSS
1
-
Typescript
10
-
Uncategorized
0
-
Vue JS
1
-
Windows Operating system
1
-
Woocommerce
1
-
WordPress Development
2
Tags
- Artificial Intelligence (AI)
- Bash Scripting
- Business
- C
- C Programming
- C-sharp programming
- C++
- Code Editor
- Computer Engineering
- CSS
- Data Structure and Algorithm
- Database
- Design pattern
- Express JS
- git
- Git Commands
- github
- HTML
- Java
- JavaScript
- Laravel
- Mathematics
- MongoDB
- Mysql
- Node JS
- PHP
- Programming
- Python
- React Js
- Redux
- Rust Programming Language
- SEO
- TypeScript
- Vue JS
- Windows terminal
- Woocommerce
- WordPress
- WordPress Plugin Development