🚀
7. Collections

Vectors

fn main() {
    let a = [1, 2, 3, 4, 5];
    let v: Vec<i32> = Vec::new();
}
  • vector can grow or shrink in size
fn main() {
    let a = [1, 2, 3, 4, 5];
    let mut v: Vec<i32> = Vec::new();
    v.push(6);
    v.push(7);
    v.push(8);
 
    let v2 = vec![1, 2, 3, 4, 5];
}
  • vec! macro creates a new vector with initial values of the inffered type
use std::vec;
 
fn main() {
    let a = [1, 2, 3, 4, 5];
    let mut v: Vec<i32> = Vec::new();
    v.push(6);
    v.push(7);
    v.push(8);
    {
        let v2 = vec![1, 2, 3, 4, 5];
    }
}
  • Refferecing elements in a vector
fn main() {
        let v2 = vec![1, 2, 3, 4, 5];
        let third: &i32 = &v2[2];
        println!("The third element is {}", third);
}
fn main () {
    let v2 = vec![1, 2, 3, 4, 5];
    let third: &i32 = &v2[20];
    println!("The third element is {}", third);
}

Runtime Error:
thread main panicked at 'index out of bounds: the len is 5 but the index is 20'

  • vector are stored in the heap, so size of a vector is not known at compile time
  • array are stored on the stack, so size of an array is known at compile time
fn main () {
    let v2 = vec![1, 2, 3, 4, 5];
    let elem: Option<&i32> = v2.get(2);
 
    match elem {
        Some(value) => println!("The element is {}", value),
        None => println!("There is no element"),
    }
}
  • get method returns Option<&T>, which is Some(&element) or None
fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
 
    let elem = &v[2];
    v.push(6);
 
    println!("The third element is {}", elem);
}

Error:
cannot borrow v as mutable because it is also borrowed as immutable

  • when we use an immutable refference to a vector, we expect to get the orginal vector back, which means that the orginal vector has to be stored somewhere else in memory
  • which cannot be done because refferenced to the modified vector

Iterating over the values in a vector

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    for i in &v {
        println!("{}", i);
    }
}
  • &v creates an immutable refference to the vector
fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
    for i in &mut v {
        *i += 50;
    }
 
    for i in &v {
        print!("{} ", i);
    }
}
  • &mut v creates a mutable refference to the vector
  • *i dereferences i to get the value in the vector
$ cargo run
 
51 52 53 54 55

Storing Enums in a Vector

fn main() {
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }
 
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Float(10.12),
        SpreadsheetCell::Text(String::from("blue")),
    ];
 
    match &row[0] {
        SpreadsheetCell::Int(value) => println!("The value is {}", value),
        _ => println!("Not an integer"),
    }
}

Strings

  • Strings are stored as a collection of UTF-8 encoded bytes
  • ASCII characters are encoded in 1 byte, while other characters are encoded in 2 bytes
  • Unicode scalar values are stored in 4 bytes
  • UTF-8 encoding allows for a string to be valid UTF-8, but not valid Unicode, size of a string is not known at compile time
fn main() {
    let hello = String::from("السلام عليكم");
    let hello = String::from("Dobrý den");
    let hello = String::from("Hello");
    let hello = String::from("שלום");
    let hello = String::from("नमस्ते");
    let hello = String::from("こんにちは");
    let hello = String::from("안녕하세요");
    let hello = String::from("你好");
    let hello = String::from("Olá");
    let hello = String::from("Здравствуйте");
    let hello = String::from("Hola");
}

Appending to a String

fn main() {
    let mut s = String::from("Hello");
    s.push_str(", World");
    s.push('!');
    println!("{}", s);
}
  • push_str appends a string slice to a string
  • push appends a single character to a string

Concatenation with the + Operator

fn main() {
    let s1 = String::from("Hello");
    let s2 = String::from(", World");
    let s3 = s1 + &s2;
    println!("{}", s3);
}
  • + operator uses the add method, which takes ownership of s1 and borrows s2
  • fn add(self, s: &str) -> String, takes in an refference to a string slice &str and returns a new string
  • s3 is the result of the concatenation, so it takes ownership of s1 and s1 is no longer valid

Formatting Strings

fn main() {
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");
 
    let s = format!("{}-{}-{}", s1, s2, s3);
    println!("{}", s);
}
  • format! macro creates a string without taking ownership of any of its parameters

Indexing into Strings

fn main() {
    let s1 = String::from("hello");
    let h = s1[0];
}

| let h = s1[0];
| ------------^ string indices are ranges of usize
|
= help: the trait SliceIndex<str> is not implemented for {integer}, which is required by String: Index<_>

use unicode_segmentation::UnicodeSegmentation;
 
fn main() {
    let s1 = String::from("नमस्ते");
 
    for b in s1.bytes() {
        print!("{} ", b);
    }
 
    for c in s1.chars() {
        print!("{} ", c);
    }
 
    for g in s1.graphemes(true) {
        print!("{} ", g);
    }
}
 
  • String is a wrapper over a Vec<u8>, so indexing into a string would return a byte, which is not a character
Bytes
  • bytes() method returns an iterator over the bytes of a string

[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135]

Scalar Values
  • chars() method returns an iterator over the scalar values of a string

['न', 'म', 'स', '्', 'त', 'े']

Grapheme Clusters
  • graphemes() in 📦 unicode-segmentation crate method returns an iterator over the grapheme clusters of a string

["न", "म", "स्", "ते"]

⚠️

Using &str[0] is not a good idea because it may not return the expected character

Hash Maps

  • A hash map stores a collection of key-value pairs
use std::collections::HashMap;
 
fn main() {
    let mut scores = HashMap::new();
 
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
 
    let team_name = String::from("Blue");
    let score = scores.get(&team_name);
 
    match score {
        Some(value) => println!("The score is {}", value),
        None => println!("There is no score"),
    }
}
  • HashMap::new() creates a new hash map
  • insert() method inserts a key-value pair into the hash map
  • get() method returns an Option<&V> where V is the value type
use std::collections::HashMap;
 
fn main() {
    let mut scores = HashMap::new();
 
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Blue"), 50);
    //The score is 50
 
    scores.entry(String::from("Yellow")).or_insert(10);
    scores.entry(String::from("Yellow")).or_insert(50);
    // The score is 10
 
}
  • entry() method returns an Entry enum
  • insert() method inserts a value, overwrites the existing value
  • or_insert() method inserts a value if the key does not exist
use std::collections::HashMap;
fn main() {
    let text = "hello world wonderful world";
 
    let mut map = HashMap::new();
 
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
 
    println!("{:?}", map);
}
  • or_insert() method returns a mutable reference to the value &mut V
  • *count += 1 dereferences count to get the value in the hash map

© 2024 Driptanil Datta.All rights reserved

Made with Love ❤️

Last updated on Mon Oct 20 2025