时间轴

2025-10-27

init


环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
# Installation
cargo install rustlings
# Initialization
rustlings init
# Moving into new directory
cd rustlings
# 进入rustlings命令行模式
rustlings
# 检查所有题目
rustlings check-all
# 运行某一个题目
rustlings run primitive_types2

variable

常量定义要加上类型!
variable6

1
2
3
4
5
6
7
// TODO: Change the line below to fix the compiler error.
const NUMBER: i32 = 3;

fn main() {
println!("Number: {NUMBER}");
}

move_semantics

vec 声明需要为 mut , 参数也得是 mut

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
vec.push(88);

vec
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn move_semantics3() {
let mut vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}

struct

..语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#[derive(Debug)]
struct Order {
name: String,
year: u32,
made_by_phone: bool,
made_by_mobile: bool,
made_by_email: bool,
item_number: u32,
count: u32,
}

fn create_order_template() -> Order {
Order {
name: String::from("Bob"),
year: 2019,
made_by_phone: false,
made_by_mobile: false,
made_by_email: true,
item_number: 123,
count: 0,
}
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn your_order() {
let order_template = create_order_template();

// TODO: Create your own order using the update syntax and template above!
// let your_order =
let your_order = Order {
name: String::from("Hacker in Rust"),
count: 1,
..order_template
};
assert_eq!(your_order.name, "Hacker in Rust");
assert_eq!(your_order.year, order_template.year);
assert_eq!(your_order.made_by_phone, order_template.made_by_phone);
assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile);
assert_eq!(your_order.made_by_email, order_template.made_by_email);
assert_eq!(your_order.item_number, order_template.item_number);
assert_eq!(your_order.count, 1);
}
}

enum

enum 可以存放的 variants 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#[derive(Debug)]
struct Point {
x: u64,
y: u64,
}

#[derive(Debug)]
enum Message {
// TODO: Define the different variants used below.
Resize { width: u32, height: u32 },
Move(Point),
Echo(String),
ChangeColor(u8, u8, u8),
Quit,
}

impl Message {
fn call(&self) {
println!("{self:?}");
}
}

fn main() {
let messages = [
Message::Resize {
width: 10,
height: 30,
},
Message::Move(Point { x: 10, y: 15 }),
Message::Echo(String::from("hello world")),
Message::ChangeColor(200, 255, 255),
Message::Quit,
];

for message in &messages {
message.call();
}
}


string

quiz2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// This is a quiz for the following sections:
// - Strings
// - Vecs
// - Move semantics
// - Modules
// - Enums
//
// Let's build a little machine in the form of a function. As input, we're going
// to give a list of strings and commands. These commands determine what action
// is going to be applied to the string. It can either be:
// - Uppercase the string
// - Trim the string
// - Append "bar" to the string a specified amount of times
//
// The exact form of this will be:
// - The input is going to be a Vector of 2-length tuples,
// the first element is the string, the second one is the command.
// - The output element is going to be a vector of strings.

enum Command {
Uppercase,
Trim,
Append(usize),
}

mod my_module {
use super::Command;

// TODO: Complete the function as described above.
// pub fn transformer(input: ???) -> ??? { ??? }
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
let mut output = vec![];
for (s, cmd) in input.iter() {
match cmd {
Command::Uppercase => {
output.push(s.to_uppercase());
}
Command::Trim => {
output.push(s.trim().to_string());
}
Command::Append(times) => {
output.push(format!("{}{}", s, "bar".repeat(*times)));
}
}
}
output
}
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
// TODO: What do we need to import to have `transformer` in scope?
// use ???;
use super::my_module::transformer;
use super::Command;

#[test]
fn it_works() {
let input = vec![
("hello".to_string(), Command::Uppercase),
(" all roads lead to rome! ".to_string(), Command::Trim),
("foo".to_string(), Command::Append(1)),
("bar".to_string(), Command::Append(5)),
];
let output = transformer(input);

assert_eq!(
output,
[
"HELLO",
"all roads lead to rome!",
"foobar",
"barbarbarbarbarbar",
]
);
}
}

hashmap

entry 方法非常好用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// We're collecting different fruits to bake a delicious fruit cake. For this,
// we have a basket, which we'll represent in the form of a hash map. The key
// represents the name of each fruit we collect and the value represents how
// many of that particular fruit we have collected. Three types of fruits -
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You
// must add fruit to the basket so that there is at least one of each kind and
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
// to insert any more of the fruits that are already in the basket (Apple,
// Mango, and Lychee).

use std::collections::HashMap;

#[derive(Hash, PartialEq, Eq, Debug)]
enum Fruit {
Apple,
Banana,
Mango,
Lychee,
Pineapple,
}

fn fruit_basket(basket: &mut HashMap<Fruit, u32>) {
let fruit_kinds = [
Fruit::Apple,
Fruit::Banana,
Fruit::Mango,
Fruit::Lychee,
Fruit::Pineapple,
];

for fruit in fruit_kinds {
// TODO: Insert new fruits if they are not already present in the
// basket. Note that you are not allowed to put any type of fruit that's
// already present!
basket.entry(fruit).or_insert(8);
}
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

// Don't modify this function!
fn get_fruit_basket() -> HashMap<Fruit, u32> {
let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)];
HashMap::from_iter(content)
}

#[test]
fn test_given_fruits_are_not_modified() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);
assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2);
assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5);
}

#[test]
fn at_least_five_types_of_fruits() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
let count_fruit_kinds = basket.len();
assert!(count_fruit_kinds >= 5);
}

#[test]
fn greater_than_eleven_fruits() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
let count = basket.values().sum::<u32>();
assert!(count > 11);
}

#[test]
fn all_fruit_types_in_basket() {
let fruit_kinds = [
Fruit::Apple,
Fruit::Banana,
Fruit::Mango,
Fruit::Lychee,
Fruit::Pineapple,
];

let mut basket = get_fruit_basket();
fruit_basket(&mut basket);

for fruit_kind in fruit_kinds {
let Some(amount) = basket.get(&fruit_kind) else {
panic!("Fruit kind {fruit_kind:?} was not found in basket");
};
assert!(*amount > 0);
}
}
}

entry().and_modify().or_insert()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// A list of scores (one per line) of a soccer match is given. Each line is of
// the form "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>"
// Example: "England,France,4,2" (England scored 4 goals, France 2).
//
// You have to build a scores table containing the name of the team, the total
// number of goals the team scored, and the total number of goals the team
// conceded.

use std::collections::HashMap;

// A structure to store the goal details of a team.
#[derive(Default)]
struct TeamScores {
goals_scored: u8,
goals_conceded: u8,
}

fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
// The name of the team is the key and its associated struct is the value.
let mut scores = HashMap::<&str, TeamScores>::new();

for line in results.lines() {
let mut split_iterator = line.split(',');
// NOTE: We use `unwrap` because we didn't deal with error handling yet.
let team_1_name = split_iterator.next().unwrap();
let team_2_name = split_iterator.next().unwrap();
let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();

// TODO: Populate the scores table with the extracted details.
// Keep in mind that goals scored by team 1 will be the number of goals
// conceded by team 2. Similarly, goals scored by team 2 will be the
// number of goals conceded by team 1.
scores
.entry(team_1_name)
.and_modify(|teamscore| {
teamscore.goals_scored += team_1_score;
teamscore.goals_conceded += team_2_score;
})
.or_insert(TeamScores {
goals_scored: team_1_score,
goals_conceded: team_2_score,
});
scores
.entry(team_2_name)
.and_modify(|teamscore| {
teamscore.goals_scored += team_2_score;
teamscore.goals_conceded += team_1_score;
})
.or_insert(TeamScores {
goals_scored: team_2_score,
goals_conceded: team_1_score,
});
}

scores
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

const RESULTS: &str = "England,France,4,2
France,Italy,3,1
Poland,Spain,2,0
Germany,England,2,1
England,Spain,1,0";

#[test]
fn build_scores() {
let scores = build_scores_table(RESULTS);

assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name)));
}

#[test]
fn validate_team_score_1() {
let scores = build_scores_table(RESULTS);
let team = scores.get("England").unwrap();
assert_eq!(team.goals_scored, 6);
assert_eq!(team.goals_conceded, 4);
}

#[test]
fn validate_team_score_2() {
let scores = build_scores_table(RESULTS);
let team = scores.get("Spain").unwrap();
assert_eq!(team.goals_scored, 0);
assert_eq!(team.goals_conceded, 3);
}
}

option

match取得所有权,可以用ref声明取得的是ref类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}

fn main() {
let optional_point = Some(Point { x: 100, y: 200 });

// TODO: Fix the compiler error by adding something to this match statement.
match optional_point {
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}

println!("{optional_point:?}"); // Don't change this line.
}

也可以直接match引用类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}

fn main() {
let optional_point = Some(Point { x: 100, y: 200 });

// TODO: Fix the compiler error by adding something to this match statement.
match &optional_point {
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}

println!("{optional_point:?}"); // Don't change this line.
}

error_handling

返回Box类型,?运算符会自动包装返回的错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// This exercise is an altered version of the `errors4` exercise. It uses some
// concepts that we won't get to until later in the course, like `Box` and the
// `From` trait. It's not important to understand them in detail right now, but
// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as
// an "I want anything that does ???" type.
//
// In short, this particular use case for boxes is for when you want to own a
// value and you care only that it is a type which implements a particular
// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where
// `Trait` is the trait the compiler looks for on any value used in that
// context. For this exercise, that context is the potential errors which
// can be returned in a `Result`.

use std::error::Error;
use std::fmt;

#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}

// This is required so that `CreationError` can implement `Error`.
impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
CreationError::Negative => "number is negative",
CreationError::Zero => "number is zero",
};
f.write_str(description)
}
}

impl Error for CreationError {}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
}

// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
// use to describe both errors? Is there a trait which both errors implement?
fn main() -> Result<(), Box<dyn Error>> {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
Ok(())
}

map_error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
// library code where callers might want to make decisions based on the error
// content instead of printing it out or propagating it further. Here, we define
// a custom error type to make it possible for callers to decide what to do next
// when our function returns an error.

use std::num::ParseIntError;

#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}

// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
ParseInt(ParseIntError),
}

impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> Self {
Self::Creation(err)
}

// TODO: Add another error conversion function here.
// fn from_parse_int(???) -> Self { ??? }
fn from_parse_int(err: ParseIntError) -> Self {
Self::ParseInt(err)
}
}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)),
}
}

fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
}
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_parse_error() {
assert!(matches!(
PositiveNonzeroInteger::parse("not a number"),
Err(ParsePosNonzeroError::ParseInt(_)),
));
}

#[test]
fn test_negative() {
assert_eq!(
PositiveNonzeroInteger::parse("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
);
}

#[test]
fn test_zero() {
assert_eq!(
PositiveNonzeroInteger::parse("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
);
}

#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42).unwrap();
assert_eq!(x.0, 42);
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
}
}

trait

trait default implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
trait Licensed {
// TODO: Add a default implementation for `licensing_info` so that
// implementors like the two structs below can share that default behavior
// without repeating the function.
// The default license information should be the string "Default license".
fn licensing_info(&self) -> String {
String::from("Default license")
}
}

struct SomeSoftware {
version_number: i32,
}

struct OtherSoftware {
version_number: String,
}

impl Licensed for SomeSoftware {} // Don't edit this line.
impl Licensed for OtherSoftware {} // Don't edit this line.

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn is_licensing_info_the_same() {
let licensing_info = "Default license";
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),
};
assert_eq!(some_software.licensing_info(), licensing_info);
assert_eq!(other_software.licensing_info(), licensing_info);
}
}

iterator

iterator是惰性的,除非调用next或collect否则什么也没发生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#[derive(Debug, PartialEq, Eq)]
enum DivisionError {
// Example: 42 / 0
DivideByZero,
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
IntegerOverflow,
// Example: 5 / 2 = 2.5
NotDivisible,
}

// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
if b == 0 {
return Err(DivisionError::DivideByZero);
}
if a == i64::MIN && b == -1 {
return Err(DivisionError::IntegerOverflow);
}
if a % b == 0 {
return Ok(a / b);
} else {
return Err(DivisionError::NotDivisible);
}
}

// TODO: Add the correct return type and complete the function body.
// Desired output: `Ok([1, 11, 1426, 3])`
fn result_with_list() -> Result<Vec<i64>, DivisionError> {
let numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
division_results.collect()
}

// TODO: Add the correct return type and complete the function body.
// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]`
fn list_of_results() -> Vec<Result<i64, DivisionError>> {
let numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
division_results.collect()
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
assert_eq!(divide(81, -1), Ok(-81));
assert_eq!(divide(i64::MIN, i64::MIN), Ok(1));
}

#[test]
fn test_divide_by_0() {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}

#[test]
fn test_integer_overflow() {
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
}

#[test]
fn test_not_divisible() {
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
}

#[test]
fn test_divide_0_by_something() {
assert_eq!(divide(0, 81), Ok(0));
}

#[test]
fn test_result_with_list() {
assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);
}

#[test]
fn test_list_of_results() {
assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]);
}
}

计算阶乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
fn factorial(num: u64) -> u64 {
// TODO: Complete this function to return the factorial of `num` which is
// defined as `1 * 2 * 3 * … * num`.
// https://en.wikipedia.org/wiki/Factorial
//
// Do not use:
// - early returns (using the `return` keyword explicitly)
// Try not to use:
// - imperative style loops (for/while)
// - additional variables
// For an extra challenge, don't use:
// - recursion
if num == 0 { 1 } else { (1..=num).product() }
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn factorial_of_0() {
assert_eq!(factorial(0), 1);
}

#[test]
fn factorial_of_1() {
assert_eq!(factorial(1), 1);
}
#[test]
fn factorial_of_2() {
assert_eq!(factorial(2), 2);
}

#[test]
fn factorial_of_4() {
assert_eq!(factorial(4), 24);
}
}

map可以把iter所要迭代的类型转换成要迭代的另一种类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Let's define a simple model to track Rustlings' exercise progress. Progress
// will be modelled using a hash map. The name of the exercise is the key and
// the progress is the value. Two counting functions were created to count the
// number of exercises with a given progress. Recreate this counting
// functionality using iterators. Try to not use imperative loops (for/while).

use std::collections::HashMap;

#[derive(Clone, Copy, PartialEq, Eq)]
enum Progress {
None,
Some,
Complete,
}

fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
let mut count = 0;
for val in map.values() {
if *val == value {
count += 1;
}
}
count
}

// TODO: Implement the functionality of `count_for` but with an iterator instead
// of a `for` loop.
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// `map` is a hash map with `String` keys and `Progress` values.
// map = { "variables1": Complete, "from_str": None, … }
map.values().filter(|&&val| val == value).count()
}

fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
let mut count = 0;
for map in collection {
for val in map.values() {
if *val == value {
count += 1;
}
}
}
count
}

// TODO: Implement the functionality of `count_collection_for` but with an
// iterator instead of a `for` loop.
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// `collection` is a slice of hash maps.
// collection = [{ "variables1": Complete, "from_str": None, … },
// { "variables2": Complete, … }, … ]
collection
.iter()
.map(|map| count_iterator(map, value))
.sum()
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

fn get_map() -> HashMap<String, Progress> {
use Progress::*;

let mut map = HashMap::new();
map.insert(String::from("variables1"), Complete);
map.insert(String::from("functions1"), Complete);
map.insert(String::from("hashmap1"), Complete);
map.insert(String::from("arc1"), Some);
map.insert(String::from("as_ref_mut"), None);
map.insert(String::from("from_str"), None);

map
}

fn get_vec_map() -> Vec<HashMap<String, Progress>> {
use Progress::*;

let map = get_map();

let mut other = HashMap::new();
other.insert(String::from("variables2"), Complete);
other.insert(String::from("functions2"), Complete);
other.insert(String::from("if1"), Complete);
other.insert(String::from("from_into"), None);
other.insert(String::from("try_from_into"), None);

vec![map, other]
}

#[test]
fn count_complete() {
let map = get_map();
assert_eq!(count_iterator(&map, Progress::Complete), 3);
}

#[test]
fn count_some() {
let map = get_map();
assert_eq!(count_iterator(&map, Progress::Some), 1);
}

#[test]
fn count_none() {
let map = get_map();
assert_eq!(count_iterator(&map, Progress::None), 2);
}

#[test]
fn count_complete_equals_for() {
let map = get_map();
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
for progress_state in progress_states {
assert_eq!(
count_for(&map, progress_state),
count_iterator(&map, progress_state),
);
}
}

#[test]
fn count_collection_complete() {
let collection = get_vec_map();
assert_eq!(
count_collection_iterator(&collection, Progress::Complete),
6,
);
}

#[test]
fn count_collection_some() {
let collection = get_vec_map();
assert_eq!(count_collection_iterator(&collection, Progress::Some), 1);
}

#[test]
fn count_collection_none() {
let collection = get_vec_map();
assert_eq!(count_collection_iterator(&collection, Progress::None), 4);
}

#[test]
fn count_collection_equals_for() {
let collection = get_vec_map();
let progress_states = [Progress::Complete, Progress::Some, Progress::None];

for progress_state in progress_states {
assert_eq!(
count_collection_for(&collection, progress_state),
count_collection_iterator(&collection, progress_state),
);
}
}
}

smart pointers

std::borrow::Cow 智能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can
// enclose and provide immutable access to borrowed data and clone the data
// lazily when mutation or ownership is required. The type is designed to work
// with general borrowed data via the `Borrow` trait.

use std::borrow::Cow;

fn abs_all(input: &mut Cow<[i32]>) {
for ind in 0..input.len() {
let value = input[ind];
if value < 0 {
// Clones into a vector if not already owned.
input.to_mut()[ind] = -value;
}
}
}

fn main() {
// You can optionally experiment here.
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn reference_mutation() {
// Clone occurs because `input` needs to be mutated.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(&vec);
abs_all(&mut input);
assert!(matches!(input, Cow::Owned(_)));
}

#[test]
fn reference_no_mutation() {
// No clone occurs because `input` doesn't need to be mutated.
let vec = vec![0, 1, 2];
let mut input = Cow::from(&vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, Cow::Borrowed(_)));
}

#[test]
fn owned_no_mutation() {
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
// case, no mutation occurs (all numbers are already absolute) and thus
// also no clone. But the result is still owned because it was never
// borrowed or mutated.
let vec = vec![0, 1, 2];
let mut input = Cow::from(vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, Cow::Owned(_)));
}

#[test]
fn owned_mutation() {
// Of course this is also the case if a mutation does occur (not all
// numbers are absolute). In this case, the call to `to_mut()` in the
// `abs_all` function returns a reference to the same data as before.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, Cow::Owned(_)));
}
}

thread

收集线程返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// This program spawns multiple threads that each runs for at least 250ms, and
// each thread returns how much time it took to complete. The program should
// wait until all the spawned threads have finished and should collect their
// return values into a vector.

use std::{
thread,
time::{Duration, Instant},
};

fn main() {
let mut handles = Vec::new();
for i in 0..10 {
let handle = thread::spawn(move || {
let start = Instant::now();
thread::sleep(Duration::from_millis(250));
println!("Thread {i} done");
start.elapsed().as_millis()
});
handles.push(handle);
}

let mut results = Vec::new();
for handle in handles {
// TODO: Collect the results of all threads into the `results` vector.
// Use the `JoinHandle` struct which is returned by `thread::spawn`.
results.push(handle.join().unwrap());
}

if results.len() != 10 {
panic!("Oh no! Some thread isn't done yet!");
}

println!();
for (i, result) in results.into_iter().enumerate() {
println!("Thread {i} took {result}ms");
}
}

线程共享可变状态。在 Rust 中,RefCell 提供的是 单线程下的内部可变性,它不能跨线程安全使用。所以把 RefCell 包在 Arc 里,是 不安全的,会在编译时或者运行时出错。要在多线程中安全地共享和修改数据,需要用 Arc>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Building on the last exercise, we want all of the threads to complete their
// work. But this time, the spawned threads need to be in charge of updating a
// shared value: `JobStatus.jobs_done`

use std::{sync::Mutex, sync::Arc, thread, time::Duration};

struct JobStatus {
jobs_done: u32,
}

fn main() {
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));

let mut handles = Vec::new();
for _ in 0..10 {
let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));

// TODO: You must take an action before you update a shared value.
status_shared.lock().unwrap().jobs_done += 1;
});
handles.push(handle);
}

// Waiting for all jobs to complete.
for handle in handles {
handle.join().unwrap();
}

// TODO: Print the value of `JobStatus.jobs_done`.
println!("Jobs done: {}", status.lock().unwrap().jobs_done);
}

image-20251029175015612