Many programming languages have tried to tackle memory management in different, better ways. Some, like C/C++, leave it up to the programmer to allocate and free memory, others like Python do it all automatically, with something called a Garbage Collector. A dev at mozilla didn’t like either approach, as both have massive issues(Leaving to the programmer may lead to them forgetting to free memory, doing automatically may lead to stutter when memory is freed), and so, in 2006, they decided to make something new.
What is Rust
Rust is a special kind of programming language that helps programmers create powerful and safe software. It helps keep mistakes in check by catching errors early, which means fewer bugs and crashes in the programs people use, and has a new style of memory management.
The way it catches errors early is by making sure you declare variable types, enforcing rules about ownership and borrowing (like sharing toys), ensuring the creation of error handlers, and using a “borrow checker” to watch over how you use variables. These checks prevent mix-ups, conflicts, and mistakes that can happen in other languages, making your programs safer and more dependable. Because of its compiled nature and low level, the performance of softwares created with rust tend to be on par with C/C++.
Installing Rust Compiler
To start programming in Rust, you need to install the Rust programming environment. Visit the official Rust website (https://www.rust-lang.org/) and follow the installation instructions for your operating system. On Windows, you just Download > Next Next Next > Finish. On Linux and MacOS you can install using the terminal by running curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
Hello, Rust!
Let’s begin by writing a simple “Hello, World!” program in Rust.
fn main() {
println!("Hello, Rust!");
}
Here, fn main()
is the entry point of the program, the first thing to execute, and println!
is used to print text to the console. As you can notice, Rust requires semicolons at the end of each line to separate them properly.
Variables and Data Types
Rust has a strong static type system. You don’t need to declare variables(simply using let
will let the compiler pick a type for you) but you can manually declare as well. The list of existing types is:
Primitive types:
- Boolean
- Numeric
- Textual
- Never
Sequence types:
- Tuple
- Array
- Slice
User-defined types:
- Struct
- Enum
- Union
Function types:
- Functions
- Closures
Pointer types:
- References
- Raw pointers
- Function pointers
Trait types:
- Trait objects
- Impl trait
Example of variable declaration:
fn main() {
let floatval = 2.0;
let number: i32 = 42;
let text: &str = "Rust is awesome!";
}
In this, floatval is a 64 bit Float variable,i32
is a 32-bit integer type, and &str
is a string slice type.
Variables are also immutable by default. To make them mutable(aka, you can change their value at any point) it’s necessary to add mut
between let
and their name:
fn main() {
let mut floatval = 2.0;
let mut number: i32 = 42;
let mut text: &str = "Rust is awesome!";
}
Now all variables are mutable.
Finally, it’s important to note that once a variable goes out of scope(a function reach its end without passing a variable back), it gets automatically erased from memory.
Functions
Functions in Rust are defined using the fn
keyword. They’re pieces of re-usable code that can be called in other parts of your code.
fn add(x: i32, y: i32) -> i32 { //defines function called add, and its return of a int 32
return x + y;
}
fn main() {
let result = add(5, 3);
println!("Result: {}", result);
}
Here, add
is a function that takes two integers and returns their sum.
Control Flow
Rust supports common control flow statements like if
, else
, while
, and for
.
fn main() {
let number = 7;
if number < 5 {
println!("Number is less than 5");
} else {
println!("Number is greater than or equal to 5");
}
}
if
, else
and else if
are for conditional branching(Unlike many languages they don’t require parentheses).
fn main(){
let mut i=0;
while i<5{
println!("{}",i);
i+=1;
}
for i in 0..5{
println!("{}",i);
}
}
while
and for
are loops, used to iterate through collections, or simply run code X many times. For loops like the above will go from the first value(in that case, 0) until the last element(5), not including it.
Collections
Rust provides collections like vectors, tuples and arrays to store multiple values.
fn main() {
let numbers = vec![1, 2, 3, 4, 5]; //Vector
let randomvalues = ("Hey", 6, 'd'); //Tuple
let morenumbers: [i32; 5] = [1, 2, 3, 4, 5]; //Array
for num in &numbers {
println!("Number: {}", num);
}
println!("{}",randomvalues.1);//access second value from tuple
println!("{}",morenumbers[1]);//access second value from array
}
In rust, arrays are a fixed size collection of values of the same type, Vector is a variable size collection of elements of the same type and tuple is a variable size collection of elements of any type.
Ownership and Borrowing
Rust’s ownership system ensures memory safety.
Ownership means that the function where a variable is created is the Owner of said variable. It can do whatever it wants with it: change its value, give it away, or even throw it away when you’re done. Only one function can own a variable at a time, so if you pass it as argument to another, that function is now the new owner.
Borrowing is when you pass a “read only” reference to a variable. When a function borrows a variable, it can read it and do work with it, but not change its value. While borrowing, the owner can’t make modifications either, to avoid conflicts.
fn main() {
let owner_string = String::from("I'm the owner!"); // You're the owner of this string.
let borrowed_length = get_length(&owner_string); // Borrowing the string to get its length.
println!("Length: {}", borrowed_length);
} // The owner_string goes out of scope and is automatically cleaned up.
// Function that borrows the string and returns its length.
fn get_length(s: &String) -> usize {
s.len()
} // Borrow ends here, but you can still use 'owner_string'.
Structs
Structs allow you to define custom data structures.
struct Person {
name: String,
age: u32,
}
fn main() {
let person = Person {
name: String::from("Alice"),
age: 30,
};
println!("{} is {} years old", person.name, person.age);
}
Enums
Enums define types with multiple possible values.
enum Color {
Red,
Green,
Blue,
}
fn main() {
let selected_color = Color::Blue;
match selected_color {
Color::Red => println!("It's red"),
Color::Green => println!("It's green"),
Color::Blue => println!("It's blue"),
}
}
Error Handling
Rust encourages explicit error handling using the Result
type. Majority of operations will require error handling so the compiler knows what to do in case of errors or exceptions.
fn divide(x: f64, y: f64) -> Result<f64, String> {
if y == 0.0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(x / y)
}
}
fn main() {
let result = divide(10.0, 2.0);
match result {
Ok(value) => println!("Result: {}", value),
Err(msg) => println!("Error: {}", msg),
}
}
Modules and Packages
Rust code is organized into modules, and multiple modules can be grouped into packages.
Packages are like gift-wrapped boxes that hold one or more modules. They allow you to share your code with others or reuse it in different projects. A package contains a special file called “Cargo.toml” where you list information about your package, like its name and any other packages it depends on. Rust’s package manager, Cargo, takes care of downloading and organizing those packages for you. This way, you can easily include external code in your project and share your own code with the world. Modules and Packages in Rust make coding more organized, efficient, and collaborative.
Modules in Rust are like compartments that help you organize your code into logical sections. Think of them as different drawers in a toolbox. They allow you to group related functions, structs, enums, and other items together, making your codebase easier to manage. Modules keep things organized by letting you control what parts of the code are visible and accessible in different parts of your program, helping you avoid confusion and errors. This separation also encourages reusability, allowing you to use the same module in multiple projects or share it with other programmers.
Conclusion
Congratulations! You’ve learned the basics of Rust programming. This tutorial covered just the essential concepts, but Rust has much more to offer. If you wish to learn more, the community around the language has made both a read-only Book and a Interactive Book that can both help a lot!
Remember, however, that practice is key to mastering programming. Happy coding in Rust! ?