## 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
```

----
#### 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>

<!--
| 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[¬_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}]"}