mirror of
https://github.com/sunface/rust-by-practice.git
synced 2025-06-25 13:39:41 +00:00
@ -11,7 +11,7 @@ The hash table implementation is a Rust port of Google’s [SwissTable](https://
|
|||||||
|
|
||||||
```rust,editable
|
```rust,editable
|
||||||
|
|
||||||
// FILL in the blanks and FIX the erros
|
// FILL in the blanks and FIX the errors
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut scores = HashMap::new();
|
let mut scores = HashMap::new();
|
||||||
@ -20,12 +20,12 @@ fn main() {
|
|||||||
scores.insert("Ashley", 69.0);
|
scores.insert("Ashley", 69.0);
|
||||||
scores.insert("Katie", "58");
|
scores.insert("Katie", "58");
|
||||||
|
|
||||||
// get returns a Option<&V>
|
// Get returns an Option<&V>
|
||||||
let score = scores.get("Sunface");
|
let score = scores.get("Sunface");
|
||||||
assert_eq!(score, Some(98));
|
assert_eq!(score, Some(98));
|
||||||
|
|
||||||
if scores.contains_key("Daniel") {
|
if scores.contains_key("Daniel") {
|
||||||
// indexing return a value V
|
// Indexing returns a value V
|
||||||
let score = scores["Daniel"];
|
let score = scores["Daniel"];
|
||||||
assert_eq!(score, __);
|
assert_eq!(score, __);
|
||||||
scores.remove("Daniel");
|
scores.remove("Daniel");
|
||||||
@ -34,7 +34,7 @@ fn main() {
|
|||||||
assert_eq!(scores.len(), __);
|
assert_eq!(scores.len(), __);
|
||||||
|
|
||||||
for (name, score) in scores {
|
for (name, score) in scores {
|
||||||
println!("The score of {} is {}", name, score)
|
println!("The score of {} is {}", name, score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -56,12 +56,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IMPLEMENT team_map2 in two ways
|
// IMPLEMENT team_map2 in two ways
|
||||||
// tips: one of the approaches is to use `collect` method
|
// Tips: one of the approaches is to use `collect` method
|
||||||
let teams_map2...
|
let teams_map2...
|
||||||
|
|
||||||
assert_eq!(teams_map1, teams_map2);
|
assert_eq!(teams_map1, teams_map2);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -71,16 +71,16 @@ fn main() {
|
|||||||
// FILL in the blanks
|
// FILL in the blanks
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
fn main() {
|
fn main() {
|
||||||
// type inference lets us omit an explicit type signature (which
|
// Type inference lets us omit an explicit type signature (which
|
||||||
// would be `HashMap<&str, u8>` in this example).
|
// would be `HashMap<&str, u8>` in this example).
|
||||||
let mut player_stats = HashMap::new();
|
let mut player_stats = HashMap::new();
|
||||||
|
|
||||||
// insert a key only if it doesn't already exist
|
// Insert a key only if it doesn't already exist
|
||||||
player_stats.entry("health").or_insert(100);
|
player_stats.entry("health").or_insert(100);
|
||||||
|
|
||||||
assert_eq!(player_stats["health"], __);
|
assert_eq!(player_stats["health"], __);
|
||||||
|
|
||||||
// insert a key using a function that provides a new value only if it
|
// Insert a key using a function that provides a new value only if it
|
||||||
// doesn't already exist
|
// doesn't already exist
|
||||||
player_stats.entry("health").or_insert_with(random_stat_buff);
|
player_stats.entry("health").or_insert_with(random_stat_buff);
|
||||||
assert_eq!(player_stats["health"], __);
|
assert_eq!(player_stats["health"], __);
|
||||||
@ -92,11 +92,11 @@ fn main() {
|
|||||||
*health -= 50;
|
*health -= 50;
|
||||||
assert_eq!(*health, __);
|
assert_eq!(*health, __);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_stat_buff() -> u8 {
|
fn random_stat_buff() -> u8 {
|
||||||
// could actually return some random value here - let's just return
|
// Could actually return some random value here - let's just return
|
||||||
// some fixed value for now
|
// some fixed value for now
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ fn main() {
|
|||||||
let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
|
let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
|
||||||
map.insert(1, 2);
|
map.insert(1, 2);
|
||||||
map.insert(3, 4);
|
map.insert(3, 4);
|
||||||
// indeed ,the capacity of HashMap is not 100, so we can't compare the equality here.
|
// Indeed ,the capacity of HashMap is not 100, so we can't compare the equality here.
|
||||||
assert!(map.capacity() >= 100);
|
assert!(map.capacity() >= 100);
|
||||||
|
|
||||||
// Shrinks the capacity of the map with a lower limit. It will drop
|
// Shrinks the capacity of the map with a lower limit. It will drop
|
||||||
@ -176,7 +176,7 @@ fn main() {
|
|||||||
// and possibly leaving some space in accordance with the resize policy.
|
// and possibly leaving some space in accordance with the resize policy.
|
||||||
map.shrink_to_fit();
|
map.shrink_to_fit();
|
||||||
assert!(map.capacity() >= 2);
|
assert!(map.capacity() >= 2);
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -196,12 +196,12 @@ fn main() {
|
|||||||
|
|
||||||
let v2 = "hello".to_string();
|
let v2 = "hello".to_string();
|
||||||
let mut m2 = HashMap::new();
|
let mut m2 = HashMap::new();
|
||||||
// ownership moved here
|
// Ownership moved here
|
||||||
m2.insert(v2, v1);
|
m2.insert(v2, v1);
|
||||||
|
|
||||||
assert_eq!(v2, "hello");
|
assert_eq!(v2, "hello");
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ The usage of third-party hash looks like this:
|
|||||||
```rust
|
```rust
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
// introduce a third party hash function
|
// Introduce a third party hash function
|
||||||
use twox_hash::XxHash64;
|
use twox_hash::XxHash64;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# String
|
# String
|
||||||
`std::string::String` is a UTF-8 encoded, growable string. It is the most common string type we used in daily dev, it also has ownership over the string contents.
|
`std::string::String` is a UTF-8 encoded, growable string. It is the most common string type we used in daily development, it also has ownership over the string contents.
|
||||||
|
|
||||||
### Basic operations
|
### Basic operations
|
||||||
1. 🌟🌟
|
1. 🌟🌟
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
// FILL in the blanks and FIX errors
|
// FILL in the blanks and FIX errors
|
||||||
// 1. Don't use `to_string()`
|
// 1. Don't use `to_string()`
|
||||||
// 2. Dont't add/remove any code line
|
// 2. Don't add/remove any code line
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut s: String = "hello, ";
|
let mut s: String = "hello, ";
|
||||||
s.push_str("world".to_string());
|
s.push_str("world".to_string());
|
||||||
@ -17,7 +17,7 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!(s, "hello, world!");
|
assert_eq!(s, "hello, world!");
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_ownership(s: String) {
|
fn move_ownership(s: String) {
|
||||||
@ -36,7 +36,7 @@ A `String` is stored as a vector of bytes (`Vec<u8>`), but guaranteed to always
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut s = String::from("hello, world");
|
let mut s = String::from("hello, world");
|
||||||
|
|
||||||
let slice1: &str = __; // in two ways
|
let slice1: &str = __; // In two ways
|
||||||
assert_eq!(slice1, "hello, world");
|
assert_eq!(slice1, "hello, world");
|
||||||
|
|
||||||
let slice2 = __;
|
let slice2 = __;
|
||||||
@ -46,37 +46,37 @@ fn main() {
|
|||||||
slice3.push('!');
|
slice3.push('!');
|
||||||
assert_eq!(slice3, "hello, world!");
|
assert_eq!(slice3, "hello, world!");
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 🌟🌟
|
3. 🌟🌟
|
||||||
```rust,editable
|
```rust,editable
|
||||||
|
|
||||||
// Question: how many heap allocations are happened here ?
|
// Question: how many heap allocations are happening here?
|
||||||
// Your answer:
|
// Your answer:
|
||||||
fn main() {
|
fn main() {
|
||||||
// Create a String type based on `&str`
|
// Create a String type based on `&str`
|
||||||
// the type of string literals is `&str`
|
// The type of string literals is `&str`
|
||||||
let s: String = String::from("hello, world!");
|
let s: String = String::from("hello, world!");
|
||||||
|
|
||||||
// create a slice point to String `s`
|
// Create a slice point to String `s`
|
||||||
let slice: &str = &s;
|
let slice: &str = &s;
|
||||||
|
|
||||||
// create a String type based on the recently created slice
|
// Create a String type based on the recently created slice
|
||||||
let s: String = slice.to_string();
|
let s: String = slice.to_string();
|
||||||
|
|
||||||
assert_eq!(s, "hello, world!");
|
assert_eq!(s, "hello, world!");
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### UTF-8 & Indexing
|
### UTF-8 & Indexing
|
||||||
Strings are always valid UTF-8. This has a few implications:
|
Strings are always valid UTF-8. This has a few implications:
|
||||||
|
|
||||||
- the first of which is that if you need a non-UTF-8 string, consider [OsString](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html). It is similar, but without the UTF-8 constraint.
|
- The first of which is that if you need a non-UTF-8 string, consider [OsString](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html). It is similar, but without the UTF-8 constraint.
|
||||||
- The second implication is that you cannot index into a String
|
- The second implication is that you cannot index into a String.
|
||||||
|
|
||||||
Indexing is intended to be a constant-time operation, but UTF-8 encoding does not allow us to do this. Furthermore, it’s not clear what sort of thing the index should return: a byte, a codepoint, or a grapheme cluster. The bytes and chars methods return iterators over the first two, respectively.
|
Indexing is intended to be a constant-time operation, but UTF-8 encoding does not allow us to do this. Furthermore, it’s not clear what sort of thing the index should return: a byte, a codepoint, or a grapheme cluster. The bytes and chars methods return iterators over the first two, respectively.
|
||||||
|
|
||||||
@ -90,22 +90,22 @@ fn main() {
|
|||||||
let slice1 = s[0]; //tips: `h` only takes 1 byte in UTF8 format
|
let slice1 = s[0]; //tips: `h` only takes 1 byte in UTF8 format
|
||||||
assert_eq!(slice1, "h");
|
assert_eq!(slice1, "h");
|
||||||
|
|
||||||
let slice2 = &s[3..5];// tips: `中` takes 3 bytes in UTF8 format
|
let slice2 = &s[3..5]; // Tips: `中` takes 3 bytes in UTF8 format
|
||||||
assert_eq!(slice2, "世");
|
assert_eq!(slice2, "世");
|
||||||
|
|
||||||
// iterate all chars in s
|
// Iterate through all chars in s
|
||||||
for (i, c) in s.__ {
|
for (i, c) in s.__ {
|
||||||
if i == 7 {
|
if i == 7 {
|
||||||
assert_eq!(c, '世')
|
assert_eq!(c, '世')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### utf8_slice
|
#### UTF8_slice
|
||||||
You can use [utf8_slice](https://docs.rs/utf8_slice/1.0.0/utf8_slice/fn.slice.html) to slice UTF8 string, it can index chars instead of bytes.
|
You can use [utf8_slice](https://docs.rs/utf8_slice/1.0.0/utf8_slice/fn.slice.html) to slice UTF8 string, it can index chars instead of bytes.
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
@ -130,16 +130,16 @@ fn main() {
|
|||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
__;
|
__;
|
||||||
|
|
||||||
// some bytes, in a vector
|
// Some bytes, in a vector
|
||||||
let v = vec![104, 101, 108, 108, 111];
|
let v = vec![104, 101, 108, 108, 111];
|
||||||
|
|
||||||
// Turn a bytes vector into a String
|
// Turn a byte's vector into a String
|
||||||
let s1 = __;
|
let s1 = __;
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(s, s1);
|
assert_eq!(s, s1);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ The pointer points to an internal buffer String uses to store its data. The leng
|
|||||||
6. 🌟🌟 If a String has enough capacity, adding elements to it will not re-allocate
|
6. 🌟🌟 If a String has enough capacity, adding elements to it will not re-allocate
|
||||||
```rust,editable
|
```rust,editable
|
||||||
|
|
||||||
// modify the code below to print out:
|
// Modify the code below to print out:
|
||||||
// 25
|
// 25
|
||||||
// 25
|
// 25
|
||||||
// 25
|
// 25
|
||||||
@ -166,7 +166,7 @@ fn main() {
|
|||||||
println!("{}", s.capacity());
|
println!("{}", s.capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ use std::mem;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let story = String::from("Rust By Practice");
|
let story = String::from("Rust By Practice");
|
||||||
|
|
||||||
// Prevent automatically dropping the String's data
|
// Prevent automatically dropping of the String's data
|
||||||
let mut story = mem::ManuallyDrop::new(story);
|
let mut story = mem::ManuallyDrop::new(story);
|
||||||
|
|
||||||
let ptr = story.__();
|
let ptr = story.__();
|
||||||
@ -188,14 +188,14 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!(16, len);
|
assert_eq!(16, len);
|
||||||
|
|
||||||
// We can re-build a String out of ptr, len, and capacity. This is all
|
// We can rebuild a String out of ptr, len, and capacity. This is all
|
||||||
// unsafe because we are responsible for making sure the components are
|
// unsafe because we are responsible for making sure the components are
|
||||||
// valid:
|
// valid:
|
||||||
let s = unsafe { String::from_raw_parts(ptr, len, capacity) };
|
let s = unsafe { String::from_raw_parts(ptr, len, capacity) };
|
||||||
|
|
||||||
assert_eq!(*story, s);
|
assert_eq!(*story, s);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Vector
|
# Vector
|
||||||
Vectors are re-sizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time.
|
Vectors are resizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time.
|
||||||
|
|
||||||
### Basic Operations
|
### Basic Operations
|
||||||
1. 🌟🌟🌟
|
1. 🌟🌟🌟
|
||||||
@ -18,14 +18,14 @@ fn main() {
|
|||||||
let v = vec!(1, 2, 3);
|
let v = vec!(1, 2, 3);
|
||||||
is_vec(v);
|
is_vec(v);
|
||||||
|
|
||||||
// in code below, v is Vec<[u8; 3]> , not Vec<u8>
|
// In code below, v is Vec<[u8; 3]> , not Vec<u8>
|
||||||
// USE Vec::new and `for` to rewrite the below code
|
// USE Vec::new and `for` to rewrite the below code
|
||||||
let v1 = vec!(arr);
|
let v1 = vec!(arr);
|
||||||
is_vec(v1);
|
is_vec(v1);
|
||||||
|
|
||||||
assert_eq!(v, v1);
|
assert_eq!(v, v1);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_vec(v: Vec<u8>) {}
|
fn is_vec(v: Vec<u8>) {}
|
||||||
@ -33,7 +33,7 @@ fn is_vec(v: Vec<u8>) {}
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. 🌟🌟 a Vec can be extended with `extend` method
|
2. 🌟🌟 A Vec can be extended with `extend` method
|
||||||
```rust,editable
|
```rust,editable
|
||||||
|
|
||||||
// FILL in the blank
|
// FILL in the blank
|
||||||
@ -47,7 +47,7 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!(v1, v2);
|
assert_eq!(v1, v2);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ fn main() {
|
|||||||
|
|
||||||
// FILL in the blanks
|
// FILL in the blanks
|
||||||
fn main() {
|
fn main() {
|
||||||
// array -> Vec
|
// Array -> Vec
|
||||||
// impl From<[T; N]> for Vec
|
// impl From<[T; N]> for Vec
|
||||||
let arr = [1, 2, 3];
|
let arr = [1, 2, 3];
|
||||||
let v1 = __(arr);
|
let v1 = __(arr);
|
||||||
@ -84,7 +84,7 @@ fn main() {
|
|||||||
let v4: Vec<i32> = [0; 10].into_iter().collect();
|
let v4: Vec<i32> = [0; 10].into_iter().collect();
|
||||||
assert_eq!(v4, vec![0; 10]);
|
assert_eq!(v4, vec![0; 10]);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!(v, vec![2, 3, 4, 5, 6]);
|
assert_eq!(v, vec![2, 3, 4, 5, 6]);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -123,13 +123,13 @@ fn main() {
|
|||||||
let mut v = vec![1, 2, 3];
|
let mut v = vec![1, 2, 3];
|
||||||
|
|
||||||
let slice1 = &v[..];
|
let slice1 = &v[..];
|
||||||
// out of bounds will cause a panic
|
// Out of bounds will cause a panic
|
||||||
// You must use `v.len` here
|
// You must use `v.len` here
|
||||||
let slice2 = &v[0..4];
|
let slice2 = &v[0..4];
|
||||||
|
|
||||||
assert_eq!(slice1, slice2);
|
assert_eq!(slice1, slice2);
|
||||||
|
|
||||||
// slice are read only
|
// Slices are read only
|
||||||
// Note: slice and &Vec are different
|
// Note: slice and &Vec are different
|
||||||
let vec_ref: &mut Vec<i32> = &mut v;
|
let vec_ref: &mut Vec<i32> = &mut v;
|
||||||
(*vec_ref).push(4);
|
(*vec_ref).push(4);
|
||||||
@ -138,7 +138,7 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!(slice3, &[1, 2, 3, 4]);
|
assert_eq!(slice3, &[1, 2, 3, 4]);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Capacity
|
### Capacity
|
||||||
@ -169,7 +169,7 @@ fn main() {
|
|||||||
assert!(vec.capacity() >= 11);
|
assert!(vec.capacity() >= 11);
|
||||||
|
|
||||||
|
|
||||||
// fill in an appropriate value to make the `for` done without reallocating
|
// Fill in an appropriate value to make the `for` done without reallocating
|
||||||
let mut vec = Vec::with_capacity(__);
|
let mut vec = Vec::with_capacity(__);
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
vec.push(i);
|
vec.push(i);
|
||||||
@ -178,12 +178,12 @@ fn main() {
|
|||||||
assert_eq!(vec.len(), __);
|
assert_eq!(vec.len(), __);
|
||||||
assert_eq!(vec.capacity(), __);
|
assert_eq!(vec.capacity(), __);
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Store distinct types in Vector
|
### Store distinct types in Vector
|
||||||
The elements in a vector mush be the same type, for example , the code below will cause an error:
|
The elements in a vector must be the same type, for example , the code below will cause an error:
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
let v = vec![1, 2.0, 3];
|
let v = vec![1, 2.0, 3];
|
||||||
@ -207,7 +207,7 @@ fn main() {
|
|||||||
assert_eq!(v[0], IpAddr::V4("127.0.0.1".to_string()));
|
assert_eq!(v[0], IpAddr::V4("127.0.0.1".to_string()));
|
||||||
assert_eq!(v[1], IpAddr::V6("::1".to_string()));
|
assert_eq!(v[1], IpAddr::V6("::1".to_string()));
|
||||||
|
|
||||||
println!("Success!")
|
println!("Success!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user