Rust Basics - HackTricks

# Rust Basics

### Generic Types

Create a struct where 1 of their values could be any type

    #![allow(unused)]
    fn main() {
    struct Wrapper<T> {
        value: T,
    }

    impl<T> Wrapper<T> {
        pub fn new(value: T) -> Self {
            Wrapper { value }
        }
    }

    Wrapper::new(42).value
    Wrapper::new("Foo").value, "Foo"
    }

### Option, Some & None

The Option type means that the value might by of type Some (there is something) or None:

    #![allow(unused)]
    fn main() {
    pub enum Option<T> {
        None,
        Some(T),
    }
    }

You can use functions such as `is_some()` or `is_none()` to check the value of the Option.

### Macros

Macros are more powerful than functions because they expand to produce more code than the code you’ve written manually. For example, a function signature must declare the number and type of parameters the function has. Macros, on the other hand, can take a variable number of parameters: we can call `println!("hello")` with one argument or `println!("hello {}", name)` with two arguments. Also, macros are expanded before the compiler interprets the meaning of the code, so a macro can, for example, implement a trait on a given type. A function can’t, because it gets called at runtime and a trait needs to be implemented at compile time.

    macro_rules! my_macro {
        () => {
            println!("Check out my macro!");
        };
        ($val:expr) => {
            println!("Look at this other macro: {}", $val);
        }
    }
    fn main() {
        my_macro!();
        my_macro!(7777);
    }

    // Export a macro from a module
    mod macros {
        #[macro_export]
        macro_rules! my_macro {
            () => {
                println!("Check out my macro!");
            };
        }
    }

### Iterate

    #![allow(unused)]
    fn main() {
    // Iterate through a vector
    let my_fav_fruits = vec!["banana", "raspberry"];
    let mut my_iterable_fav_fruits = my_fav_fruits.iter();
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
    assert_eq!(my_iterable_fav_fruits.next(), None); // When it's over, it's none

    // One line iteration with action
    my_fav_fruits.iter().map(|x| capitalize_first(x)).collect()

    // Hashmap iteration
    for (key, hashvalue) in &*map {
    for key in map.keys() {
    for value in map.values() {
    }

### Recursive Box

    #![allow(unused)]
    fn main() {
    enum List {
        Cons(i32, List),
        Nil,
    }

    let list = Cons(1, Cons(2, Cons(3, Nil)));
    }

### Conditionals

#### if

    #![allow(unused)]
    fn main() {
    let n = 5;
    if n < 0 {
        print!("{} is negative", n);
    } else if n > 0 {
        print!("{} is positive", n);
    } else {
        print!("{} is zero", n);
    }
    }

#### match

    #![allow(unused)]
    fn main() {
    match number {
        // Match a single value
        1 => println!("One!"),
        // Match several values
        2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
        // TODO ^ Try adding 13 to the list of prime values
        // Match an inclusive range
        13..=19 => println!("A teen"),
        // Handle the rest of cases
        _ => println!("Ain't special"),
    }

    let boolean = true;
    // Match is an expression too
    let binary = match boolean {
        // The arms of a match must cover all the possible values
        false => 0,
        true => 1,
        // TODO ^ Try commenting out one of these arms
    };
    }

#### loop (infinite)

    #![allow(unused)]
    fn main() {
    loop {
        count += 1;
        if count == 3 {
            println!("three");
            continue;
        }
        println!("{}", count);
        if count == 5 {
            println!("OK, that's enough");
            break;
        }
    }
    }

#### while

    #![allow(unused)]
    fn main() {
    let mut n = 1;
    while n < 101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
        n += 1;
    }
    }

#### for

    #![allow(unused)]
    fn main() {
    for n in 1..101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else {
            println!("{}", n);
        }
    }

    // Use "..=" to make inclusive both ends
    for n in 1..=100 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }

    // ITERATIONS

    let names = vec!["Bob", "Frank", "Ferris"];
    //iter - Doesn't consume the collection
    for name in names.iter() {
        match name {
            &"Ferris" => println!("There is a rustacean among us!"),
            _ => println!("Hello {}", name),
        }
    }
    //into_iter - COnsumes the collection
    for name in names.into_iter() {
        match name {
            "Ferris" => println!("There is a rustacean among us!"),
            _ => println!("Hello {}", name),
        }
    }
    //iter_mut - This mutably borrows each element of the collection
    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        }
    }
    }

#### if let

    #![allow(unused)]
    fn main() {
    let optional_word = Some(String::from("rustlings"));
    if let word = optional_word {
        println!("The word is: {}", word);
    } else {
        println!("The optional word doesn't contain anything");
    }
    }

#### while let

    #![allow(unused)]
    fn main() {
    let mut optional = Some(0);
    // This reads: "while `let` destructures `optional` into
    // `Some(i)`, evaluate the block (`{}`). Else `break`.
    while let Some(i) = optional {
        if i > 9 {
            println!("Greater than 9, quit!");
            optional = None;
        } else {
            println!("`i` is `{:?}`. Try again.", i);
            optional = Some(i + 1);
        }
        // ^ Less rightward drift and doesn't require
        // explicitly handling the failing case.
    }
    }

### Traits

Create a new method for a type

    #![allow(unused)]
    fn main() {
    trait AppendBar {
        fn append_bar(self) -> Self;
    }

    impl AppendBar for String {
        fn append_bar(self) -> Self{
            format!("{}Bar", self)
        }
    }

    let s = String::from("Foo");
    let s = s.append_bar();
    println!("s: {}", s);
    }

### Tests

    #![allow(unused)]
    fn main() {
    #[cfg(test)]
    mod tests {
        #[test]
        fn you_can_assert() {
            assert!(true);
            assert_eq!(true, true);
            assert_ne!(true, false);
        }
    }
    }

### Threading

#### Arc

An Arc can use Clone to create more references over the object to pass them to the threads. When the last reference pointer to a value is out of scope, the variable is dropped.

    #![allow(unused)]
    fn main() {
    use std::sync::Arc;
    let apple = Arc::new("the same apple");
    for _ in 0..10 {
        let apple = Arc::clone(&apple);
        thread::spawn(move || {
            println!("{:?}", apple);
        });
    }
    }

#### Threads

In this case we will pass the thread a variable it will be able to modify

    fn main() {
        let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
        let status_shared = Arc::clone(&status);
        thread::spawn(move || {
            for _ in 0..10 {
                thread::sleep(Duration::from_millis(250));
                let mut status = status_shared.lock().unwrap();
                status.jobs_completed += 1;
            }
        });
        while status.lock().unwrap().jobs_completed < 10 {
            println!("waiting... ");
            thread::sleep(Duration::from_millis(500));
        }
    }