时间轴
时间轴
2025-10-27
init
本文介绍了 Rust 编程语言练习项目 rustlings 的学习笔记,涵盖了环境搭建、变量定义、move 语义、结构体更新语法、枚举类型、字符串处理、HashMap 的 entry 方法、Option 与 match 模式匹配、错误处理(包括 Box 类型与 ? 运算符)、trait 默认实现、迭代器的惰性特性及 map 方法、智能指针(如 Cow)以及多线程中共享可变状态的安全方案。文章总结了 Rust 中常见易错点与实用技巧,例如常量定义需显式标注类型、vec 与参数需声明为 mut、match 可通过引用绑定避免所有权转移、RefCell 不能跨线程使用,而应使用 Arc 结合其他同步类型实现线程安全的数据共享。
环境搭建
123456789101112 | # Installationcargo install rustlings# Initializationrustlings init# Moving into new directorycd rustlings# 进入rustlings命令行模式rustlings# 检查所有题目rustlings check-all# 运行某一个题目rustlings run primitive_types2 |
variable
常量定义要加上类型!
variable6
1234567 | // TODO: Change the line below to fix the compiler error.const NUMBER: i32 = 3;fn main() { println!("Number: {NUMBER}");} |
move_semantics
vec 声明需要为 mut,参数也得为 mut
123456789101112131415161718192021 | fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> { vec.push(88); vec}fn main() { // You can optionally experiment here.}mod tests { use super::*; fn move_semantics3() { let mut vec0 = vec![22, 44, 66]; let vec1 = fill_vec(vec0); assert_eq!(vec1, [22, 44, 66, 88]); }} |
struct
…更新语法
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 | 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.}mod tests { use super::*; 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 类型
12345678910111213141516171819202122232425262728293031323334353637383940 | struct Point { x: u64, y: u64,}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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 | // 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.}mod tests { // TODO: What do we need to import to have `transformer` in scope? // use ???; use super::my_module::transformer; use super::Command; 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 方法非常好用
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 | // 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;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.}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) } 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); } 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); } fn greater_than_eleven_fruits() { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); let count = basket.values().sum::<u32>(); assert!(count > 11); } 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()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 | // 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.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.}mod tests { use super::*; const RESULTS: &str = "England,France,4,2France,Italy,3,1Poland,Spain,2,0Germany,England,2,1England,Spain,1,0"; 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))); } 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); } 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绑定取得引用而非值
1234567891011121314151617 | 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引用类型
1234567891011121314151617 | 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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | // 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;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 {}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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 | // 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;enum CreationError { Negative, Zero,}// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.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) }}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.}mod test { use super::*; fn test_parse_error() { assert!(matches!( PositiveNonzeroInteger::parse("not a number"), Err(ParsePosNonzeroError::ParseInt(_)), )); } fn test_negative() { assert_eq!( PositiveNonzeroInteger::parse("-555"), Err(ParsePosNonzeroError::Creation(CreationError::Negative)), ); } fn test_zero() { assert_eq!( PositiveNonzeroInteger::parse("0"), Err(ParsePosNonzeroError::Creation(CreationError::Zero)), ); } 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
12345678910111213141516171819202122232425262728293031323334353637383940 | 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.}mod tests { use super::*; 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否则什么也没发生
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 | 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.}mod tests { use super::*; 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)); } fn test_divide_by_0() { assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } fn test_integer_overflow() { assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow)); } fn test_not_divisible() { assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); } fn test_divide_0_by_something() { assert_eq!(divide(0, 81), Ok(0)); } fn test_result_with_list() { assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); } fn test_list_of_results() { assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); }} |
计算阶乘
123456789101112131415161718192021222324252627282930313233343536373839404142 | 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.}mod tests { use super::*; fn factorial_of_0() { assert_eq!(factorial(0), 1); } fn factorial_of_1() { assert_eq!(factorial(1), 1); } fn factorial_of_2() { assert_eq!(factorial(2), 2); } fn factorial_of_4() { assert_eq!(factorial(4), 24); }} |
map可以把iter所要迭代的类型转换成要迭代的另一种类型
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 | // 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;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.}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] } fn count_complete() { let map = get_map(); assert_eq!(count_iterator(&map, Progress::Complete), 3); } fn count_some() { let map = get_map(); assert_eq!(count_iterator(&map, Progress::Some), 1); } fn count_none() { let map = get_map(); assert_eq!(count_iterator(&map, Progress::None), 2); } 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), ); } } fn count_collection_complete() { let collection = get_vec_map(); assert_eq!( count_collection_iterator(&collection, Progress::Complete), 6, ); } fn count_collection_some() { let collection = get_vec_map(); assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); } fn count_collection_none() { let collection = get_vec_map(); assert_eq!(count_collection_iterator(&collection, Progress::None), 4); } 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 智能指针
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 | // 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 `ToOwned` 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.}mod tests { use super::*; 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(_))); } 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(_))); } 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(_))); } 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
收集线程返回值
1234567891011121314151617181920212223242526272829303132333435363738 | // 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<Mutex
12345678910111213141516171819202122232425262728293031323334 | // 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);} |

