Language: EN

cheatsheet-rust

Rust Cheatsheet

Rust is a systems programming language developed by Mozilla. It is known for its safety, performance, and concurrency.

Installation and Setup

Install Rust

Rust uses a tool called rustup for installation and version management.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Check Installation

Verify that Rust and Cargo (the package manager) are installed correctly.

rustc --version
cargo --version

Update Rust

To keep Rust updated:

rustup update

Install Additional Tools

Rust supports linters and formatters like rustfmt and clippy.

rustup component add rustfmt
rustup component add clippy

Basic Structure of a Program

Hello, World

The simplest program in Rust prints “Hello, World” to the console.

fn main() {
    println!("Hello, World!");
}

Basic Project Structure

Rust uses Cargo to manage projects. Create a new project:

cargo new project_name

The main file will be in src/main.rs, and the project will include a Cargo.toml to define dependencies and configurations.

Compile and Run

cargo build      # Just compiles
cargo run        # Compiles and runs
cargo check      # Checks for compilation errors without generating binary

Variables and Data Types

Variable Declaration

let declares a variable. By default, they are immutable, but mut can be added to make them mutable.

let name = "John"; // Immutable variable
let mut age = 30; // Mutable variable

Constant Declaration

const declares a constant. It must have an explicit type, and its value cannot change.

const PI: f64 = 3.14159;

Immutability

By default, variables in Rust are immutable. To make them mutable, mut is used.

let x = 5;        // Immutable
let mut y = 10;   // Mutable

Basic Types

Rust is a strongly typed language. Some of the basic types are:

  • Integers: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128
  • Floating Points: f32, f64
  • Booleans: bool
  • Characters: char
let number: i32 = 42;
let name: &str = "Rust";
let floating: f64 = 3.14;
let is_true: bool = true;
let character: char = 'R';

String

String

Strings in Rust (String) are dynamically managed and can be modified.

let mut greeting = String::from("Hello");
greeting.push_str(", world!");

str

Static string.

let static_text: &str = "Hello, world!";

Control Flow

Conditionals

if-else

The if conditional in Rust is used similarly to other languages.

if x > 5 {
    println!("x is greater than 5");
} else {
    println!("x is less than or equal to 5");
}

match

Allows pattern matching.

let number = 5;
match number {
    1 => println!("It's one"),
    2..=5 => println!("It's between 2 and 5"),
    _ => println!("Another number"),
}

Loops

for Loop

Loop to iterate over ranges and collections.

for i in 1..5 {
    println!("i: {}", i);  // i takes values from 1 to 4
}

while Loop

Loop that runs while a condition is met.

let mut counter = 0;
while counter < 5 {
    println!("counter: {}", counter);
    counter += 1;
}

loop

Infinite loop.

loop {
    println!("This loop is infinite");
    break;  // Ends the loop
}

Functions

Function Definition

Functions in Rust are declared with fn and can return values. The types of parameters and return types must be specified explicitly.

fn sum(a: i32, b: i32) -> i32 {
    a + b
}

let result = sum(5, 3);

Closures

Closures in Rust are anonymous functions that can capture variables from their surrounding environment.

let add_one = |x| x + 1;

let result = add_one(5);
println!("Result: {}", result); // Result: 6

Data Structures

Tuples

Tuples are collections of elements of different types with a fixed size.

let tuple: (i32, f64, &str) = (42, 3.14, "Hello");
let (x, y, z) = tuple;
println!("x: {}, y: {}, z: {}", x, y, z); // x: 42, y: 3.14, z: Hello

Structs

Structs allow you to create custom data types with multiple fields.

struct Person {
    name: String,
    age: i32,
}

let john = Person {
    name: String::from("John"),
    age: 30,
};

println!("Name: {}, Age: {}", john.name, john.age);

Vectors

Vectors are collections of elements of the same type that can grow or shrink in size.

let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);

HashMap

HashMaps store key-value pairs.

use std::collections::HashMap;

let mut map = HashMap::new();
map.insert(String::from("key"), 10);

Traits

Traits in Rust define common behaviors that a type can implement. It is similar to interfaces in other languages.

trait Greetable {
    fn greet(&self) -> String;
}

struct Person {
    name: String,
}

impl Greetable for Person {
    fn greet(&self) -> String {
        format!("Hello, I am {}", self.name)
    }
}

let john = Person { name: String::from("John") };
println!("{}", john.greet()); // Hello, I am John

Ownership and References

Rust manages memory with an ownership system that ensures safety at compile time.

Ownership Rules

  1. Each value in Rust has a unique owner.
  2. When the owner goes out of scope, the value is dropped.
  3. References to the data can be passed, but they must comply with borrowing and lifetime rules.

Move Data

Moving transfers the ownership of the value.

let s1 = String::from("Hello");
let s2 = s1;  // `s1` is no longer valid, ownership passes to `s2`

Clone Data

If you need to copy the data, clone is used.

let s1 = String::from("Hello");
let s2 = s1.clone();  // Deep copy of `s1`

References and Borrowing

Rust ensures memory safety through the Ownership system. Each value in Rust has an owner, and there can only be one owner at a time.

To borrow a value without transferring ownership, references are used.

fn print(s: &String) {
    println!("{}", s);
}
let string = String::from("Hello");
print(&string);  // Passes a reference, does not transfer ownership

Lifetimes

The concept of lifetimes in Rust ensures that references do not outlive the data they point to.

fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

Error Handling

Rust handles errors explicitly through the Result type and the Option type.

Result

Result is used for operations that can fail. It has two variants: Ok and Err.

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

Option

Option is used for values that can be null (None).

fn find_value(vec: &Vec<i32>, x: i32) -> Option<usize> {
    vec.iter().position(|&val| val == x)
}

Concurrency in Rust

Rust offers safe concurrency with threads and allows using advanced techniques like channels and locks to avoid race conditions.

Threads

Rust uses the std::thread package to create and manage threads.

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..5 {
            println!("Hello from the thread");
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap();
}

Messaging Between Threads with Channels

Rust provides channels (channels) for communication between threads.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let value = String::from("Message");
        tx.send(value).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Received: {}", received);
}

Macros in Rust

Rust has support for macros that allow generating code at compile time.

Define Macros

macro_rules! my_macro {
    ($val:expr) => {
        println!("The value is: {}", $val);
    };
}
my_macro!(10);

Building and Testing in Rust

Rust facilitates development through integrated testing and conditional compilation.

Unit Tests

#[cfg(test)]
mod tests {
    #[test]
    fn basic_test() {
        assert_eq!(2 + 2, 4);
    }
}

Run Tests

cargo test

Cargo and Dependency Management

Cargo.toml

The Cargo.toml file manages dependencies and project configurations.

[dependencies]
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }