## Rust Programming Language Ch.8 Common Collections <p style="text-align: center;">Matt Wang</p> <p style="text-align: center;">slides: <a href="https://hackmd.io/@mattwang44/BJ6KDktBo">https://hackmd.io/@mattwang44/BJ6KDktBo</i></span> <style> span.blue { color: #337AB7 !important; } code.orange { color: #F7A004 !important; } p { font-size: 30px; text-align: left; } </style> --- ## Collections - Allocated on the heap -> size can grow or shrink - Covered topics: - Vector - String - Hashmap --- ## Vector Denoted as `Vec<T>`, only store values of the same type. ---- ### Vector Creation ```rust= // with `Vec::new()` let _v1: Vec<i32> = Vec::new(); // with `Vec::from()` let _v2 = Vec::from([1, 2, 3]); // with vec! macro let _v3 = vec![1, 2, 3]; // init + alloc let _v4 = vec![0; 3]; // equivalent to vec, [0, 0, 0] ``` ---- ### supplementary -- capacity *[doc of capacity](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.with_capacity) ``` // *with capacity specified let _v2: Vec<i32> = Vec::with_capacity(10); ``` ---- ### Print'em! ```rust /* Note that `Display` trait is behind {}, while `Debug` is for {:?} */ for i in &_v2 { println!("{}", i); // results in three lines } println!("{:?}", _v2); // this gives [1, 2, 3] ``` ---- ### Vector Freed when out of Scope ```rust { let v = vec![1, 2, 3, 4]; } // <- v & its elements are freed here ``` ---- ### Vector Insertion & Deletion ```rust // Need to be mutable for insertion // Vec<i32> annotation is not needed for `v` let mut v = Vec::new(); v.push(1); // vec infers the type from data v.push(2); // error: expected `i32`, found floating-point number // v.push(3.1); // extend from iterator v.extend([3, 4, 5].iter().copied()); ``` ---- ### Vector Insertion & Deletion ```rust // append v2 to v, v2 become empty let mut v2 = vec![4, 5, 6]; v.append(&mut v2); // remove & return the last element, or return None if empty v.pop(); ``` ---- ### Reading elements from Vector ```rust let v = vec![1, 2, 3, 4, 5]; // method 1: indexing (subscript operator) let third: &i32 = &v[2]; println!("The third element is {}", third); // method 2: get() method let third: Option<&i32> = v.get(2); match third { Some(third) => println!("The third element is {}", third), None => println!("There is no third element."), } ``` ---- ### Reading elements from Vector Choose between indexing and <code>get()</code> method based on expected behavior on non-existed index. ```rust let v = vec![1, 2, 3, 4, 5]; // method 1: indexing let does_not_exist = &v[100]; // panic // method 2: get() method let does_not_exist = v.get(100); // None ``` ---- Ch4: You can’t have mutable and immutable references in the same scope ```rust let mut v = vec![1, 2, 3, 4, 5]; let first = &v[0]; v.push(6); println!("The first element is: {}", first); ``` ```rust error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable --> src/main.rs:6:5 | 4 | let first = &v[0]; | - immutable borrow occurs here 5 | 6 | v.push(6); | ^^^^^^^^^ mutable borrow occurs here 7 | 8 | println!("The first element is: {}", first); | ----- immutable borrow later used here ``` ---- ### Store an Enum to handle multiple types ```rust enum SpreadsheetCell { Int(i32), Float(f64), Text(String), } let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from("blue")), SpreadsheetCell::Float(10.12), ]; ``` --- ## String - Stored as a collection of UTF-8 encoded bytes. - A <code class="orange">String</code> is a wrapper over a Vector of 8-bit unsigned int (`Vec<u8>`). ---- ### ASCII v.s. Unicode v.s UTF-8 - [ASCII](https://en.wikipedia.org/wiki/ASCII): covers only common English characters. - [Unicode](https://en.wikipedia.org/wiki/Unicode): covers millions of code point and modern computers use it instead of ASCII. - [UTF-8](https://en.wikipedia.org/wiki/UTF-8): variable-length character encoding based on Unicode. ---- ### What's a String? The term `string` in Rust is usually refers to: <ul> <li><p>String slice: [Ch4] references to some UTF-8 encoded string data</p></li> <li><p><code class="orange">String</code> type: a growable, mutable, owned, UTF-8 encoded string type</p></li> </ul> ---- #### String Creation ```rust let mut s = String::new(); // method 1 let data = "initial contents"; // string literal let s = data.to_string(); // based on `Display` trait // method 2 let s = "initial contents".to_string(); ``` ```rust let s = String::from("initial contents"); ``` ---- #### Updating a String - append ```rust let mut s = String::from("fo"); s.push('o'); // results in "foo" s.push_str("bar"); // results in "foobar" ``` <p><code class="orange">push_str()</code> does not take ownership.</p> ```rust let mut s1 = String::from("foo"); let s2 = "bar"; s1.push_str(s2); println!("s2 is {}", s2); // this is valid ``` ---- #### Updating a String - concatenate ```rust let s1 = String::from("tic"); let s2 = String::from("tac"); let s3 = String::from("toe"); ``` <p style="text-align: left;">method 1: <code class="orange"> + operator</code></p> ```rust // note s1 has been moved here and can no longer be used let s = s1 + "-" + &s2 + "-" + &s3; println!("{}", s1); // not valid ``` <p style="text-align: left;">method 2: <code class="orange">format!</code> macro</p> ```rust // works like `println!` let s = format!("{}-{}-{}", s1, s2, s3); println!("{}", s1); // valid ``` ---- #### Rust strings don’t support indexing <!-- need ref --> ```rust let s1 = String::from("hello"); let h = s1[0]; // raise error ``` ![](https://i.imgur.com/PBpWcX8.png) ---- #### Internal Representation of String <p><code class="orange">len()</code> indicates the length in bytes</p> ```rust let hello1 = String::from("Hola"); // 4 char println!("{}", hello1.len()); // 4 bytes ``` ```rust let hello2 = String::from("Здравствуйте"); // 12 char println!("{}", hello2.len()); // 24 bytes ``` <p>-> Index to string does not always correlated the position of bytes</p> ```rust let s = &hello2[0..4]; println!("{}", s); // Зд let s = &hello2[0..1]; // -> panic ``` ---- <p> Take a Hindi word as an example: </p> ![](https://i.imgur.com/SFo9fgi.png) <!-- | type | value | | -------- | -------- | | readable script | नमस्ते | | Vec<u8> | [224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135] | | Unicode scalar <br>(Rust `char`) | ['न', 'म', 'स', '्', 'त', 'े'] | | Grapheme Cluster <br>(字型群集, similar to letter) | ["न", "म", "स्", "ते"] | --> ---- #### Iteration with `chars()` & `bytes()` ```rust for c in "Зд".chars() { println!("{}", c); } /* З д */ ``` ```rust for c in "Зд".bytes() { println!("{}", c); } /* 208 151 208 180 */ ``` Rust std lib does not support getting grapheme clusters from strings, use crates instead (e.g. [unicode-segmentation](https://crates.io/crates/unicode-segmentation)). --- ## Hashmap - Denoted as `HashMap<K, V>`. - Adopt [SipHash](https://en.wikipedia.org/wiki/SipHash) as hashing function, which designed for ensuring security. - Ch.10 covers cusomizing hasher. ---- ### Create a HashMap Keys or values need to be in the same type. ```rust use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); // each of followings raises error // scores.insert(10, 50); // scores.insert(String::from("Yellow"), String::from("50")); ``` ---- ### Access value Get value by key with `.get()`: ```rust let team_name = String::from("Blue"); let score = scores.get(&team_name).copied().unwrap_or(0); // `.get(k: &K)` -> Option<&V> // `.get(k: &K).copied()` -> Option<i32> // `.get(k: &K).copied().unwrap_or(v: &V)` -> i32 ``` Get value by key with subscript operator: ```rust println!("{}", scores[&team_name]); let not_exists = String::from("Blues"); println!("{}", scores[&not_exists]); // -> panic! ``` ---- ### Access value Iterate over hashmap: ```rust for (key, value) in &scores { println!("{}: {}", key, value); } /* Yellow: 50 Blue: 10 */ ``` Print (Debug trait) ```rust println!("{:?}", scores) // {"Blue": 10, "Yellow": 50} ``` ---- ### Ownership ```rust let key = String::from("Favorite number"); let val = 10; let mut map = HashMap::new(); ``` Values of types with `Copy` trait (e.g. `i32`) are copied into map, <br>while values of those without (e.g. `String`) are moved. ```rust map.insert(key, val); println!("{}", key); // -> raises error println!("{}", val); ``` or you can set the reference as key: ```rust map.insert(&key, val); println!("{}", key); // Favorite number println!("{}", val); // 10 ``` ---- #### Update a HashMap ```rust let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); // overwrite scores.insert(String::from("Blue"), 20); // insert only when the key is absent scores.entry(String::from("Blue")).or_insert(50); ``` where <code class="orange">.entry()</code> returns a enum named <code class="orange">Entry</code> ([doc](https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html)). ```rust println!("{:?}", scores.entry(String::from("Blue"))); // Entry(OccupiedEntry { key: "Blue", value: 10, .. }) println!("{:?}", scores.entry(String::from("Yellow"))); // Entry(VacantEntry("Yellow")) ``` ---- ### Update based on old value ```rust 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); // {"world": 2, "hello": 1, "wonderful": 1} ``` --- ## Questions?
{"metaMigratedAt":"2023-06-17T13:57:40.575Z","metaMigratedFrom":"YAML","title":"Rust Programming Language - Ch.8 Common Collections","breaks":true,"description":"Rust Programming Language - Ch.8 Common Collections","contributors":"[{\"id\":\"a0c0ce66-fccf-4b0f-af3f-361da4df9291\",\"add\":21885,\"del\":11843}]"}
    701 views