Cover image for rustlings

rustlings

字数 4.6k
阅读
访客

时间轴

时间轴

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.}#[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

…更新语法

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
#[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 类型

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

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.}#[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 方法非常好用

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;#[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()

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.#[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,2France,Italy,3,1Poland,Spain,2,0Germany,England,2,1England,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绑定取得引用而非值

1234567891011121314151617
#[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引用类型

1234567891011121314151617
#[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类型,?运算符会自动包装返回的错误类型

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

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

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.}#[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否则什么也没发生

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
#[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)]);    }}

计算阶乘

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.}#[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所要迭代的类型转换成要迭代的另一种类型

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;#[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 智能指针

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.}#[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

收集线程返回值

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);}

arc1 练习运行结果,输出 Jobs done: 10
arc1 练习运行结果,输出 Jobs done: 10

评论加载中…