Collections: Vectors, Strings, and HashMaps

Learn to use common collection types like Vectors (resizable arrays), Strings (UTF-8 encoded text), and HashMaps (key-value pairs).


Collections: Strings in Rust

Introduction to Strings in Rust

In Rust, strings are UTF-8 encoded text. This means they can represent characters from any language, making them extremely versatile for handling text data.

Creating Strings

Rust provides different ways to create strings:

  • String Literal (&str): Immutable string slices that are often hardcoded in your program. They are stored directly in the program's binary. They are efficient but cannot be modified.
  • String: A growable, mutable, owned string. It's allocated on the heap and can be resized. This is the type you'll typically use when you need to modify a string.

Example: Creating a String

 let mut s = String::new(); // Create an empty String

                let data = "initial contents";
                let s = data.to_string();   // Convert a &str to a String
                // or
                let s = String::from("initial contents"); // Another way to convert a &str to a String 

Manipulating Strings

The String type provides methods for modifying and working with string data.

  • Appending to a String:
     let mut s = String::from("foo");
                            s.push_str("bar"); // Appends a string slice (&str)
                            s.push('!');        // Appends a single character (char) 
  • Concatenation:

    You can use the + operator to concatenate strings. Note that it takes ownership of the first string (String) and borrows the second string slice (&str). This means the first string is moved and can no longer be used after the concatenation.

     let s1 = String::from("Hello, ");
                            let s2 = String::from("world!");
                            let s3 = s1 + &s2; // s1 has been moved here and can no longer be used
    
                             //Note: the type of s2 is &String but the &String is coerced to a &str when we call the add method
                             // This is because the add method is defined to take a &str, not a &String 

    For more complex concatenation or when you want to avoid taking ownership, use the format! macro.

     let s1 = String::from("tic");
                            let s2 = String::from("tac");
                            let s3 = String::from("toe");
    
                            let s = format!("{}-{}-{}", s1, s2, s3); //No ownership is taken 
  • Replacing Substrings:
     let mut s = String::from("Hello World");
                         s.replace("World", "Rust"); // Returns a new String with the replacement 

Working with String Data

Strings in Rust are UTF-8 encoded, so it's important to understand how to access and iterate over characters.

  • Iterating over Characters:
     let s = String::from("नमस्ते"); // Hindi greeting
    
                            for c in s.chars() {
                                println!("{}", c);
                            } 
  • Iterating over Bytes:
     let s = String::from("नमस्ते");
    
                         for b in s.bytes() {
                            println!("{}", b); // Prints the UTF-8 byte values
                         } 
  • Slicing strings
     let s = String::from("hello world");
                        let hello = &s[0..5]; // hello is now &str referencing "hello"
    
                       //Note: you must be careful when slicing Rust strings,
                       // because it can crash your program if the characters are not encoded in a byte width that you'd expect 

Common String Operations

  • Getting the Length of a String:
     let s = String::from("Hello");
                            let length = s.len(); // Returns the length in bytes
                            println!("Length: {}", length); 
  • Checking if a String is Empty:
     let s = String::new();
                        let is_empty = s.is_empty();
                        println!("Is empty? {}", is_empty); 
  • Converting to Lowercase/Uppercase:
     let s = String::from("Hello World");
                            let lowercase = s.to_lowercase(); // Returns a new String in lowercase
                            let uppercase = s.to_uppercase(); // Returns a new String in uppercase 

Important Considerations

  • UTF-8 Encoding: Rust strings are always valid UTF-8. Be aware of this when performing operations that might invalidate the encoding, such as slicing.
  • Ownership: Pay close attention to ownership rules, especially when concatenating strings or passing them to functions.
  • String vs. &str: Understand the difference between the owned String and the string slice &str, and choose the appropriate type for your use case.