# Welcome to My Rough Rust Notes!

So, you’ve dipped your toes into Rust—perhaps by watching a few YouTube tutorials, reading a book, or experimenting with some code. But now, as you start coding, you find yourself constantly Googling or searching into StackOverflow for basic code. This ever-growing Rust cheat sheet is designed to save you time by compiling common code snippets for everyday Rust programming. Hopefully, it will save you time as well. Let's begin!

# Check the data type of a variable

Rust provides a function `std::any::type_name::<T>()` that returns the name of the type as a string.

```rust
use std::any::type_name;
fn type_of<T>(_: &T) -> &'static str {
    type_name::<T>()
}
let s = String::from("Hello, World!");
let x = s.chars().nth(0);
println!("x is of type: {}", type_of(&x)); 
//x is of type: core::option::Option<char>
```

# Using std::fmt

The fmt module provides functionality for formatting and printing, including traits like Display and Debug which are used to control how types are formatted when printed. Let’s see a example:

```rust
use std::fmt;
struct Point {
    x: i32,
    y: i32,
}
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
impl fmt::Debug for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)
    }
}
fn main() {
    let point = Point { x: 1, y: 2 };
    println!("Display: {}", point);
    println!("Debug: {:?}", point);
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731793542324/03cf048f-c027-4e29-8945-c2684bf94a3e.png align="center")

`fmt::Formatter` contains the state necessary for formatting. It is passed as a mutable reference to the fmt method when implementing formatting traits like Display and Debug. It provides methods to write formatted data to the output. `fmt::Result` is an alias for Result&lt;(), std::fmt::Error&gt;. It is the return type for the fmt method, indicating whether the formatting operation was successful or if an error occurred.

# Minimum and Maximum

Use **std::cmp::min(a,b) to** compare two values a and b. This function works with any type that implements the Ord trait, such as integer, float and other comparable types. Similarly cmp::max() works.

For float-point numbers f32 or f64, you can use it’s function min.

```rust
let min  = std::cmp::min(a,b);
let max  = std::cmp::max(a,b);

a.min(b);
a.max(b);
```

The cmp method in Rust returns an ordering result which can be Less, Equal or Greater.

```rust
//Signature of cmp
fn cmp(&self, other: &Self) -> Ordering
let comparison = a.cmp(&b);
match comparison {
    std::cmp::Ordering::Less => println!("a is less than b"),
    std::cmp::Ordering::Equal => println!("a is equal to b"),
    std::cmp::Ordering::Greater => println!("a is greater than b"),
    }
```

# Vector

A `Vec<T>` in Rust is a growable, heap-allocated, contiguous sequence of elements of type `T`.

```rust
let mut v: Vec<i32> = Vec::new(); //Empty Vector
let v = vec![1, 2, 3]; //Using the vec! Macro 
v.retain(|&x| x % 2 == 0); // Keeps only even numbers
//Remove all instances of a value in place 
v.retain(|&x| x != val);

//Iterating Over Vectors
for val in &v { //Iterating by Reference
    println!("{}", val);
}
for val in &mut v { //Iterating by Mutable Reference
    *val *= 2;
}
for val in v { //Consuming the Vector
    println!("{}", val);
}
// v cannot be used after this point
let v1 = vec![1, 2, 3];
let v2 = v1; // v1 is moved to v2
// v1 is no longer accessible
//Only one mutable reference or any number of immutable references.
```

## Minimum, Maximum and Sum of a Vector

Use the `iter()` method combined with the `max()` and `min()` functions, `min()` and `max()` are methods from the `Iterator` trait that return `Option<&T>`. The result will be `Some(value)` if the vector is non-empty, or `None` if the vector is empty. The values need to be unwrapped using pattern matching.

```rust
let mut v = vec![80, 50, 10, 20, 45, 30];
match v.iter().min() {
    Some(min_value) => println!("Minimum value: {}", min_value),
    None => println!("The vector is empty!"),
}
// Using let Some
if let Some(max_value) = v.iter().max() {
    println!("Maximum value: {}", max_value);
}
// Sum the elements using sum()
let total: i32 = v.iter().sum();
let total: i32 = v.iter().fold(0, |acc, &x| acc + x);
```

## Slicing of Vector

Let’s say you want to iterate over a vector starting from second element (i.e., excluding the first element):

```rust
let mut v = vec![80, 50, 10, 20, 45, 30];
//Using Slice of vector
for i in &v[1..] {
    println!("{}", i);
}

//Using Mutable Slices for Modification
for i in &mut v[1..] {
    *i += 5;  // To modify each element dereference i with *i
}

//Using Index-Based Looping
for idx in 1..v.len() {
    println!("Index: {}, Value: {}", idx, v[idx]);
}
//Using Enumerate with Skip
for (idx, val) in v.iter().enumerate().skip(1) {
    println!("Index: {}, Value: {}", idx, val);
}
```

`&v[1..]` : This creates an immutable slice of `v` starting from index `1` to the end.

## Sorting

The `sort()` method sorts the elements of the vector ***in place*** in ascending order. The elements must implement the `Ord` trait.

If you want to sort with a custom comparator, you can use the `sort_by()` method, which takes a closure to determine the order of elements.

```rust
v.sort();  // Sorts in ascending order
v.sort_by(|a, b| b.cmp(a)); // Sorting in descending order
```

Let’s say you want to sort vector of vectors based on some conditions such as the first element of each inner vector, or sum of elements etc., you can use sort\_by() method. Here are a few examples:

```rust
// Sorts by the first element
vec_of_vecs.sort_by(|a, b| a[0].cmp(&b[0]));
// Sorts by the length of inner vectors
vec_of_vecs.sort_by(|a, b| a.len().cmp(&b.len()));
// Sorts by sum of elements
vec_of_vecs.sort_by(|a, b| a.iter().sum::<i32>().cmp(&b.iter().sum::<i32>()));
```

# HashMap

The `HashMap` in Rust is a key-value store from the `std::collections` module. You can use the `get()` method, which returns an `Option`.

```rust
let mut m:HashMap<i32,i32> = HashMap::new(); //Declaring a HashMap
m.insert(1,1); //Inserting Values into a HashMap
m.insert(2,2);
let n = 2;
//Accessing Values from a HashMap using match
match m.get(&n){
    Some(value) => {println!("Value for 'n': {}", *value);}
    None => { //Key does not exist
        let value = 2;
        m.insert(n,value);
        }
    }
//Accessing Values using if let
if let Some(value) = m.get(2) {
    println!("Value for 2: {}", value);
} else {
    println!("Key not found");
}

//Using contains_key
if m.contains_key(2) {
    println!("Key exists");
} else {
    println!("Key does not exist");
}
// Removing Values from a HashMap
let removed_value = m.remove(2);
println!("{:?}", removed_value);
```

## Find the key with the maximum value

You can use the `iter()` method to iterate over the key-value pairs and use the `max_by_key()` method to find the pair with the highest value.

```rust
// Find the key with the maximum value
if let Some((k, v)) = m.iter().max_by_key(|entry| entry.1) {
    println!("max key and maximum value is: {}: {}", k, v);
} else {
    println!("The HashMap is empty.");
}
```

`max_by_key(|entry| entry.1)` finds the key-value pair with the maximum value. Here, `entry` is a tuple `(&key, &value)`, and `entry.1` accesses the value.

## .entry()

It returns an `Entry` enum, which represents either a vacant entry (`Vacant`) if the key is not present or an occupied entry (`Occupied`) if the key is already present. `or_insert(value)` inserts `value` into the map if the key is not already present and returns a mutable reference to the value in the map (either the existing one or the newly inserted one).

```rust
let mut map = HashMap::new();
// Access the entry for the key 'a'
map.entry('a').or_insert(0);
// Now the map contains {'a': 0}
let count = map.entry('a').or_insert(0);
*count += 1; // Increment the count for 'a'
```

## .get\_mut()

`get_mut()` retrieves a **mutable reference** to a value in a `HashMap`. It returns an `Option<&mut V>`, so you can modify the value if the key exists (`Some(&mut value)`).

```rust
let mut m: HashMap<i32, Vec<i32>> = HashMap::new();    
// Initialize the HashMap with empty vectors
for i in 0..5 {
    m.insert(i, Vec::new());
}
//my_vec: Vec<Vec<i32>>
for i in &my_vec {
    if let Some(t) = m.get_mut(&i[0]) { //get_mut() retrieves a mutable reference 
        t.push(i[1]);
   }
}
```

# BinaryHeap

`BinaryHeap` is a **max-heap**, meaning the largest element is always at the top.

**Heap Operations:**

* `push(value)`: Inserts a value into the heap.
    
* `pop()`: Removes and returns the greatest value (for max-heap).
    
* `peek()`: Returns a reference to the greatest value without removing it.
    

```rust
let mut heap = BinaryHeap::new();
 // Insert elements into the heap
heap.push(5);
let vec = vec![5, 1, 10, 3];
let heap2 = BinaryHeap::from(vec); //vec is no longer usable after this
 //Using collect with an Iterator
let heap3: BinaryHeap<_> = vec.into_iter().collect();
```

## Finding the Kth Largest Element

```rust
fn find_kth_largest(nums: Vec<i32>, k: usize) -> i32 {
    let mut heap = BinaryHeap::from(nums);
    for _ in 0..k - 1 {
        heap.pop();
    }
    heap.pop().unwrap()
}
```

# HashSet

A `HashSet<T>` is an unordered collection of unique values of type `T`, implemented as a hash table.

```rust
let mut set: HashSet<i32> = HashSet::new();
let vec = vec![1, 2, 3, 4];
let set: HashSet<_> = vec.into_iter().collect();
//Using the From Trait
let set = HashSet::from([1, 2, 3, 4]);

//adding elements
set.insert(77);
//Removing Elements
set.remove(&77); //Returns true if the element was present.

//Checking for Elements
if set.contains(&77) {
    println!("Set contains 77.");
}
```

## Set Operations

```rust
let set_a = HashSet::from([1, 2, 3]);
let set_b = HashSet::from([3, 4, 5]);
//Returns an iterator
let union: HashSet<_> = set_a.union(&set_b).cloned().collect();
let intersection: HashSet<_> = set_a.intersection(&set_b).cloned().collect();
let difference: HashSet<_> = set_a.difference(&set_b).cloned().collect();
//Returns an iterator over elements that are in either set_a or set_b but not in both.
let sym_diff: HashSet<_> = set_a.symmetric_difference(&set_b).cloned().collect();

//Subset and Superset Checks
if set_a.is_subset(&set_b) {
    println!("set_a is a subset of set_b");
}
if set_a.is_superset(&set_b) {
    println!("set_a is a superset of set_b");
}
```

## Convert a `HashSet` to a `Vec`

A `HashSet` is unordered, so when you collect its elements into a `Vec`, the order in the resulting vector is arbitrary. This is straightforward using the `iter()` method and the `collect()` function.

`.cloned()` creates an iterator that yields owned copies of the elements.

```rust
let vec: Vec<i32> = hash_set.iter().cloned().collect();
//Using into_iter() 
//This moves the elements out of the HashSet and into the vector.
let vec: Vec<i32> = hash_set.into_iter().collect();
```

# String

The Rust String and str types represent text using the UTF-8 encoding form, which means they can contain characters that are more than one byte in size. Because of this, you cannot directly index into a `String` or `&str` using the square bracket syntax like `s[1]` or `s[2]`.

## Access Characters at a Specific Position

The `chars()` method returns an iterator over the characters (`char` type) of a string. You can use the `nth()` method to get the character at a specific index. `nth()` returns an `Option<char>`, so you need to handle the case where the index is out of bounds. For example, `nth(1)` retrieves the character at index 1 (second character). This method accounts for multi-byte characters and is safe for UTF-8 strings. *The time complexity of* `nth()` *is O(n), as it iterates through the string up to the nth character.*

```rust
let s = String::from("Hello");
let c1 = s.chars().nth(1);
let c2 = s.chars().nth(2);
```

You can collect the characters into a vector and then index directly. If you need frequent random access the can prefer this method.

```rust
let s = String::from("Hello");
let chars: Vec<char> = s.chars().collect();
let c1 = chars[1];
```

## Remove Spaces from a String

```rust
let s = String::from("Hello World!");
//Using the replace Method
let s_no_spaces = s.replace(" ", "");
```

You can iterate over each character, filter out the spaces, and collect the result into a new `String`.

```rust
let s1: String = s.chars().filter(|&c| c != ' ').collect();

let s2: String = s.chars().filter(|c| !c.is_whitespace()).collect();
//Using the retain Method
s.retain(|c| c != ' ');
```

Methods that consume the string (like `replace` and `filter`) create a new `String`, so you need to assign the result to a new variable.

The `is_whitespace()` method checks for Unicode whitespace characters, making it robust for international text. Using `retain` modifies the string in place and can be more efficient since it doesn't create an intermediate collection.

## **Transforming Strings: Lowercase Conversion and Filtering**

```rust
let mut s = String::from("Hello, World! 123");
s.retain(|c| c.is_alphanumeric());
s = s.to_ascii_lowercase();
```

You can iterate over each character in the string, convert it to lowercase, filter out non-alphanumeric characters, and collect the result into a new `String`.

```rust
let s = String::from("Hello, World! 123");
let cleaned: String = s
    .chars()
    .filter(|c| c.is_alphanumeric())
    .map(|c| c.to_ascii_lowercase())
    .collect();
```

## Split a String by Spaces

The `split_whitespace()` method splits a string slice (`&str`) on Unicode whitespace characters and returns an iterator over the substrings. The type `Vec<&str>` means the vector contains string slices referencing the original string `s`. `split_whitespace()` automatically ignores leading and trailing whitespace. `split_whitespace()` splits on any Unicode whitespace character, including spaces, tabs (`\t`), newlines (`\n`), and carriage returns (`\r`).

```rust
let words: Vec<&str> = s.split_whitespace().collect();
//If You Need Vec<String>
let words: Vec<String> = s
        .split_whitespace()
        .map(|s| s.to_string())
        .collect();
let words: Vec<&str> = s.split(' ').collect();
```

`split()` method can be used with a space delimiter. But consecutive spaces result in empty strings (`""`) in the output. `split(' ')` will include empty strings.

```rust
let words: Vec<&str> = s.split(' ').collect();
//To filter out empty strings
let words: Vec<&str> = s
        .split(' ')
        .filter(|&s| !s.is_empty())
        .collect();
```

## Joining a Vector of Strings

You can use the `join()` method on a `Vec<String>` or a `Vec<&str>`. This method concatenates the elements of the vector, placing a separator between them.

```rust
let words = vec!["Hello", "world"];
let sentence = words.join(" ");
words[1] = "Rust";
// The sentence remains unchanged
println!("{}", sentence);  // Output: Hello world
// Print the modified vector
println!("{:?}", words);  // Output: ["Hello", "Rust"]
```

The `join()` method does **not** consume the vector so you can access or modify it later. `join()` creates a **new, separate copy** of the joined string, and any changes to the original vector will not affect the `sentence` string.

## **Building a Character Frequency HashMap from a String**

```rust
let mut freq_map = HashMap::new();
for c in s.chars() {
    *freq_map.entry(c).or_insert(0) += 1;
}
```

# Iterators

Iterators are lazy, they don't compute anything until they are consumed.

# **Ownership**, **reference and** **mutability**

## Passing references

Use `&` (e.g., `&v` for immutable reference, `&mut v` for mutable reference). If you pass `&v`, you are passing a **borrowed immutable reference** of `v`. **When accepting a reference in a function**, declare the parameter as `&Type` to signify that the function will accept an immutable reference.

```rust
fn my_function(v: &Vec<i32>)
fn modify_vector(v: &mut Vec<i32>)
```

`&mut v` creates a **mutable reference** to the vector `v`. You can modify `v` directly through this reference. The original `v` remains owned by the original scope, but you have mutable access to it.

# Conclusion

Keep practicing to get better at Rust. If any syntax or code that you use a lot in your daily coding, please leave a comment! I'll add new sections based on your feedback!
