缄戈 发表于 3 天前

rustlings 学习随笔

序言

rustlings 是一个关于rust的练习题的项目.可以帮助大家通过完成一个项目的方式练习rust的语法,我认为对于补充我rust现学现卖过程中的情况很有帮助.
下边是GPT对它的介绍:
Rustlings 是专为那些想要学习 Rust 编程语言的人设计的一个交互式练习集合。无论你是编程新手还是有经验的开发者,Rustlings 都能提供一个友好的环境来探索 Rust 的独特功能。
特点:

[*]互动性: 通过实际编写代码并即时看到结果,你可以更好地理解 Rust 的工作原理。
[*]渐进式难度: 练习按照难易程度排序,从基础到高级逐步引导你深入 Rust。
[*]涵盖广泛: 练习覆盖了 Rust 的主要方面,包括所有权、借用、生命周期、错误处理等。
[*]社区支持: 作为一个活跃的开源项目,Rustlings 拥有一个热情的支持社区,你可以在这里找到帮助或贡献自己的力量。
[*]易于安装: 只需几个简单的命令,就可以在你的机器上设置好 Rustlings,并开始你的学习之旅。
structs2

// structs2.rs
//
// Address all the TODOs to make the tests pass!
//
// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

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

#
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);
    }
}这里注意这个,这里有一个结构体更新语法的问题:
let your_order = Order {
            name: String::from("Hacker in Rust"),
            count: 1,
            ..order_template
      };string4

主要是分辨String和&str的区别.
hashmaps3

// hashmaps3.rs
//
// 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, goals the
// team scored, and goals the team conceded. One approach to build the scores
// table is to use a Hashmap. The solution is partially written to use a
// Hashmap, complete it to pass the test.
//
// Make me pass the tests!
//
// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

use std::collections::HashMap;

// A structure to store the goal details of a team.
struct Team {
    goals_scored: u8,
    goals_conceded: u8,
}

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

    for r in results.lines() {
      let v: Vec<&str> = r.split(',').collect();
      let team_1_name = v.to_string();
      let team_1_score: u8 = v.parse().unwrap();
      let team_2_name = v.to_string();
      let team_2_score: u8 = v.parse().unwrap();
      // TODO: Populate the scores table with details extracted from the
      // current line. Keep in mind that goals scored by team_1
      // will be the number of goals conceded from team_2, and similarly
      // goals scored by team_2 will be the number of goals conceded by
      // team_1.
      let team_1 = scores.entry(team_1_name).or_insert(Team {
            goals_scored: 0,
            goals_conceded: 0,
      });
      team_1.goals_scored += team_1_score;
      team_1.goals_conceded += team_2_score;
      let team_2 = scores.entry(team_2_name).or_insert(Team {
            goals_scored: 0,
            goals_conceded: 0,
      });
      team_2.goals_scored += team_2_score;
      team_2.goals_conceded += team_1_score;
    }
    scores
}

#
mod tests {
    use super::*;

    fn get_results() -> String {
      let results = "".to_string()
            + "England,France,4,2\n"
            + "France,Italy,3,1\n"
            + "Poland,Spain,2,0\n"
            + "Germany,England,2,1\n";
      results
    }

    #
    fn build_scores() {
      let scores = build_scores_table(get_results());

      let mut keys: Vec<&String> = scores.keys().collect();
      keys.sort();
      assert_eq!(
            keys,
            vec!["England", "France", "Germany", "Italy", "Poland", "Spain"]
      );
    }

    #
    fn validate_team_score_1() {
      let scores = build_scores_table(get_results());
      let team = scores.get("England").unwrap();
      assert_eq!(team.goals_scored, 5);
      assert_eq!(team.goals_conceded, 4);
    }

    #
    fn validate_team_score_2() {
      let scores = build_scores_table(get_results());
      let team = scores.get("Spain").unwrap();
      assert_eq!(team.goals_scored, 0);
      assert_eq!(team.goals_conceded, 2);
    }
}自动解引用

Deref 解引用 - Rust语言圣经(Rust Course)
所有权借用

#TODO
options2

// options2.rs
//
// Execute `rustlings hint options2` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

#
mod tests {
    #
    fn simple_option() {
      let target = "rustlings";
      let optional_target = Some(target);

      // TODO: Make this an if let statement whose value is "Some" type
      if let Some(word) = optional_target {
            assert_eq!(word, target);
      }
    }

    #
    fn layered_option() {
      let range = 10;
      let mut optional_integers: Vec<Option<i8>> = vec!;

      for i in 1..(range + 1) {
            optional_integers.push(Some(i));
      }

      let mut cursor = range;

      // TODO: make this a while let statement - remember that vector.pop also
      // adds another layer of Option<T>. You can stack `Option<T>`s into
      // while let and if let.
      while let Some(Some(integer)) = optional_integers.pop() {
            assert_eq!(integer, cursor);
            cursor -= 1;
      }

      assert_eq!(cursor, 0);
    }
}这里注意pop本身返回的就是Option,而当初push进去的成员是Some(),因此导致了需要套两层Some来做模式匹配.
options3

这里存在一个所有权的问题:
// options3.rs
//
// Execute `rustlings hint options3` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

struct Point {
    x: i32,
    y: i32,
}

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

    match y {
      Some(ref p) => println!("Co-ordinates are {},{} ", p.x, p.y),
      _ => panic!("no match!"),
    }
    y; // Fix without deleting this line.
}必须加上ref,不然会造成y本身因为被使用因此被move了所有权,那么match结束的时候就会被释放掉,因此只传入它的ref.
errors3

(名词)的传播
返回值 Result 和? - Rust语言圣经(Rust Course)
errors4

PartialEq Trait:

[*]PartialEq 是 Rust 标准库中的一个 trait,它允许你定义类型之间的相等性比较。
[*]当你为一个结构体或枚举派生 PartialEq 时,编译器会自动生成一个 == 和 != 操作符的实现,这些操作符将基于结构体或枚举的所有字段进行逐个比较。
[*]如果所有字段都相等,则认为这两个实例是相等的;如果有任何一个字段不相等,则认为它们不相等。
Debug Trait

[*]Debug 是另一个标准库中的 trait,它用于格式化调试输出。
[*]当你为一个结构体或枚举派生 Debug 时,编译器会自动生成 fmt::Debug 的实现,这使得你可以使用 {:?} 或 {:#?} 格式说明符来打印该类型的实例。
[*]这对于调试非常有用,因为它可以帮助你查看结构体或枚举实例的内容。
error5

// errors5.rs
//
// This program uses an altered version of the code from errors4.
//
// This exercise 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, which,
// given Rust's usual standards for runtime safety, should strike you as
// somewhat lenient!
//
// 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.
//
// What can we use to describe both errors? In other words, is there a trait
// which both errors implement?
//
// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

use std::error;
use std::fmt;
use std::num::ParseIntError;

// TODO: update the return type of `main()` to make this compile.
fn main() -> Result<(), Box<dyn error::Error>> {
    let pretend_user_input = "42";
    let x: i64 = pretend_user_input.parse()?;
    println!("output={:?}", PositiveNonzeroInteger::new(x)?);
    Ok(())
}

// Don't change anything below this line.

#
struct PositiveNonzeroInteger(u64);

#
enum CreationError {
    Negative,
    Zero,
}

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

// This is required so that `CreationError` can implement `error::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::Error for CreationError {}这里是用dyn error::Error代表的是一个实现了error::Error的struct,而不需要知道这个struct的名字.那么这个对象由编译器在下文中寻找.
意思就是说我不管你这个对象叫什么,只需要实现这个Trait就行了,我需要用到的是你这个struct关于这个Trait的特征.
特征对象 - Rust语言圣经(Rust Course):
特征对象指向实现了 Draw 特征的类型的实例,也就是指向了 Button 或者 SelectBox 的实例,这种映射关系是存储在一张表中,可以在运行时通过特征对象找到具体调用的类型方法。
可以通过 & 引用或者 Box 智能指针的方式来创建特征对象。
那么Box是一种创建特征对象的方式.
对于Box智能指针本身,有解释,我的理解暂时是在堆上创建一个对象,可以看作包裹的内容是保存在堆上的对象的一个引用.
traits4

这里参考了:捋捋 Rust 中的 impl Trait 和 dyn Trait - 知乎 (zhihu.com)
由于输入的时候不能输入Box包裹,可以直接用impl Trait而不是Box.
但是返回值不能是impl Trait,因为不能返回不同类型,必须加一个包裹.
// traits4.rs
//
// Your task is to replace the '??' sections so the code compiles.
//
// Don't change any line other than the marked one.
//
// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

pub trait Licensed {
    fn licensing_info(&self) -> String {
      "some information".to_string()
    }
}

struct SomeSoftware {}

struct OtherSoftware {}

impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}

// YOU MAY ONLY CHANGE THE NEXT LINE
fn compare_license_types(software: impl Licensed, software_two: impl Licensed) -> bool {
    software.licensing_info() == software_two.licensing_info()
}

#
mod tests {
    use super::*;

    #
    fn compare_license_information() {
      let some_software = SomeSoftware {};
      let other_software = OtherSoftware {};

      assert!(compare_license_types(some_software, other_software));
    }

    #
    fn compare_license_information_backwards() {
      let some_software = SomeSoftware {};
      let other_software = OtherSoftware {};

      assert!(compare_license_types(other_software, some_software));
    }
}traits5

通过+选定对两个特征的要求.
// traits5.rs
//
// Your task is to replace the '??' sections so the code compiles.
//
// Don't change any line other than the marked one.
//
// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a
// hint.

pub trait SomeTrait {
    fn some_function(&self) -> bool {
      true
    }
}

pub trait OtherTrait {
    fn other_function(&self) -> bool {
      true
    }
}

struct SomeStruct {}
struct OtherStruct {}

impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}
impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}

// YOU MAY ONLY CHANGE THE NEXT LINE
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
    item.some_function() && item.other_function()
}

fn main() {
    some_func(SomeStruct {});
    some_func(OtherStruct {});
}quiz3

这里考察的是可以给泛型的每个类型单独写方法.
// quiz3.rs
//
// This quiz tests:
// - Generics
// - Traits
//
// An imaginary magical school has a new report card generation system written
// in Rust! Currently the system only supports creating report cards where the
// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
// school also issues alphabetical grades (A+ -> F-) and needs to be able to
// print both types of report card!
//
// Make the necessary code changes in the struct ReportCard and the impl block
// to support alphabetical report cards. Change the Grade in the second test to
// "A+" to show that your changes allow alphabetical grades.
//
// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint.

pub struct ReportCard<T> {
    pub grade: T,
    pub student_name: String,
    pub student_age: u8,
}

impl ReportCard<f32> {
    pub fn print(&self) -> String {
      format!("{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade)
    }
}

impl ReportCard<&str> {
    pub fn print(&self) -> String {
      format!("{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade)
    }
}

#
mod tests {
    use super::*;

    #
    fn generate_numeric_report_card() {
      let report_card = ReportCard {
            grade: 2.1,
            student_name: "Tom Wriggle".to_string(),
            student_age: 12,
      };
      assert_eq!(
            report_card.print(),
            "Tom Wriggle (12) - achieved a grade of 2.1"
      );
    }

    #
    fn generate_alphabetic_report_card() {
      // TODO: Make sure to change the grade here after you finish the exercise.
      let report_card = ReportCard {
            grade: "A+",
            student_name: "Gary Plotter".to_string(),
            student_age: 11,
      };
      assert_eq!(
            report_card.print(),
            "Gary Plotter (11) - achieved a grade of A+"
      );
    }
}lifetimes1

认识生命周期 - Rust语言圣经(Rust Course)
// lifetimes1.rs
//
// The Rust compiler needs to know how to check whether supplied references are
// valid, so that it can let the programmer know if a reference is at risk of
// going out of scope before it is used. Remember, references are borrows and do
// not own their own data. What if their owner goes out of scope?
//
// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
      x
    } else {
      y
    }
}

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is '{}'", result);
}tests4

这个报错过不了啊!!!!
// tests4.rs
//
// Make sure that we're testing for the correct conditions!
//
// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

struct Rectangle {
    width: i32,
    height: i32
}

impl Rectangle {
    // Only change the test functions themselves
    pub fn new(width: i32, height: i32) -> Self {
      if width <= 0 || height <= 0 {
            panic!("Rectangle width and height cannot be negative!")
      }
      Rectangle {width, height}
    }
}

#
mod tests {
    use super::*;

    #
    fn correct_width_and_height() {
      // This test should check if the rectangle is the size that we pass into its constructor
      let rect = Rectangle::new(10, 20);
      assert_eq!(rect.width, 10); // check width
      assert_eq!(rect.height, 20); // check height
    }

    #
    fn negative_width() {
      // This test should check if program panics when we try to create rectangle with negative width
      // let _rect = Rectangle::new(-10, 10);
    }

    #
    fn negative_height() {
      // This test should check if program panics when we try to create rectangle with negative height
      // let _rect = Rectangle::new(10, -10);
    }
}iterators4

区间表达式 - Rust 参考手册 中文版 (rustwiki.org)
// iterators1.rs
//
// When performing operations on elements within a collection, iterators are
// essential. This module helps you get familiar with the structure of using an
// iterator and how to go through elements within an iterable collection.
//
// Make me compile by filling in the `???`s
//
// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

fn main() {
    let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];

    let mut my_iterable_fav_fruits = my_fav_fruits.iter();   // TODO: Step 1

    assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"custard apple"));   // TODO: Step 2
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"peach"));   // TODO: Step 3
    assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
    assert_eq!(my_iterable_fav_fruits.next(), None);   // TODO: Step 4
}iterator5

这里存在一个认知问题,就是还是存在对:

[*]引用和自动解引用和*
[*]for的语法糖
[*]借用
不熟悉
// iterators3.rs
//
// This is a bigger exercise than most of the others! You can do it! Here is
// your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass.
// 2. Get the remaining tests to pass by completing the result_with_list and
//    list_of_results functions.
//
// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

#
pub enum DivisionError {
    NotDivisible(NotDivisibleError),
    DivideByZero,
}

#
pub struct NotDivisibleError {
    dividend: i32,
    divisor: i32,
}

// Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
    if b == 0{
      return Err(DivisionError::DivideByZero);
    }
    let r = a/b;
    if r*b==a{
      Ok(r)
    }else{
      Err(DivisionError::NotDivisible(NotDivisibleError{dividend:a,divisor:b}))
    }
}

// Complete the function and return a value of the correct type so the test
// passes.
// Desired output: Ok()
fn result_with_list() -> Result<Vec<i32>,DivisionError> {
    let numbers = vec!;
    let division_results = numbers.into_iter().map(|n| divide(n, 27));
    division_results.collect::<Result<Vec<i32>, DivisionError>>()
}

// Complete the function and return a value of the correct type so the test
// passes.
// Desired output:
fn list_of_results() -> Vec<Result<i32, DivisionError>> {
    let numbers = vec!;
    let division_results = numbers.into_iter().map(|n| divide(n, 27));
    division_results.collect::<Vec<Result<i32, DivisionError>>>()
}

#
mod tests {
    use super::*;

    #
    fn test_success() {
      assert_eq!(divide(81, 9), Ok(9));
    }

    #
    fn test_not_divisible() {
      assert_eq!(
            divide(81, 6),
            Err(DivisionError::NotDivisible(NotDivisibleError {
                dividend: 81,
                divisor: 6
            }))
      );
    }

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

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

    #
    fn test_result_with_list() {
      assert_eq!(format!("{:?}", result_with_list()), "Ok()");
    }

    #
    fn test_list_of_results() {
      assert_eq!(
            format!("{:?}", list_of_results()),
            ""
      );
    }
}cow1

这里如果像我一样很难理解高级语言,那么我们可以去看它的实现.
这里Cow实现了两个Trait,而且是在泛型的情况下为专门的类型适配了专门的特性.
这里这两个from就引起了我的思考:

[*]&和之间有什么引用和引用的目标之间的自动转换.
[*]尤其是println!输出多层引用的时候误导了我.
[*]Deref 解引用使得这种误导更加严重,Rust中的 实现了Deref 的类型.

[*]Cow的from是根据类型实现的泛型.
根据这一节所学,可以看到本来就是在讨论Cow在面对引用和引用的目标时候有不同的表现,因此应该不是自动进行了解引用:

[*]Cow对&的from实现
[*]Creates a Borrowed variant of Cow from a slice. 可以看到,是创建了一个Borrowed.

[*]Cow对Vec的实现
[*]Creates an Owned variant of Cow from an owned instance of Vec. 可以看到是创建了一个Owned.

这里看源码看了半天找不到的原因是没有弄明白&,,Vec,vec!的区别.

[*]首先是数组Vec是可变数组,是不同的.
[*]&是一个数组的引用.
[*]vec!是一个宏,返回的是Vec.
这里必须提到,Cow没有实现对于的from,所以其中使用的是vec!返回的Vec.
例子中,一会使用&一会使用Vec给了我非常大的误导
这里还有一个遗漏的点,数组切片.
let mut input = Cow::from(&slice[..]); 这一句用的就是数组切片.
// iterators4.rs
//
// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

pub fn factorial(num: u64) -> u64 {
    // Complete this function to return the factorial of num
    // Do not use:
    // - return
    // Try not to use:
    // - imperative style loops (for, while)
    // - additional variables
    // For an extra challenge, don't use:
    // - recursion
    // Execute `rustlings hint iterators4` for hints.
    (1..=num).product()
}

#
mod tests {
    use super::*;

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

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

    #
    fn factorial_of_4() {
      assert_eq!(24, factorial(4));
    }
}threads1

如果大家学过其它语言的多线程,可能就知道不同语言对于线程的实现可能大相径庭:

[*]由于操作系统提供了创建线程的 API,因此部分语言会直接调用该 API 来创建线程,因此最终程序内的线程数和该程序占用的操作系统线程数相等,一般称之为1:1 线程模型,例如 Rust。
[*]还有些语言在内部实现了自己的线程模型(绿色线程、协程),程序内部的 M 个线程最后会以某种映射方式使用 N 个操作系统线程去运行,因此称之为M:N 线程模型,其中 M 和 N 并没有特定的彼此限制关系。一个典型的代表就是 Go 语言。
[*]还有些语言使用了 Actor 模型,基于消息传递进行并发,例如 Erlang 语言。
总之,每一种模型都有其优缺点及选择上的权衡,而 Rust 在设计时考虑的权衡就是运行时(Runtime)。出于 Rust 的系统级使用场景,且要保证调用 C 时的极致性能,它最终选择了尽量小的运行时实现。
有时候只看一本书是不够的,还需要多看几本书.
// iterators5.rs
//
// 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 not to use imperative loops (for, while).
// Only the two iterator methods (count_iterator and count_collection_iterator)
// need to be modified.
//
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

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
}

fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
    // map is a hashmap with String keys and Progress values.
    // map = { "variables1": Complete, "from_str": None, ... }
    map.values().filter(|v| *v==&value).count()
}

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

fn count_collection_iterator(collection: &, value: Progress) -> usize {
    // collection is a slice of hashmaps.
    // collection = [{ "variables1": Complete, "from_str": None, ... },
    //   { "variables2": Complete, ... }, ... ]
    collection.into_iter().map(|c| count_iterator(c,value)).sum()
}

#
mod tests {
    use super::*;

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

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

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

    #
    fn count_complete_equals_for() {
      let map = get_map();
      let progress_states = vec!;
      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!(
            6,
            count_collection_iterator(&collection, Progress::Complete)
      );
    }

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

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

    #
    fn count_collection_equals_for() {
      let progress_states = vec!;
      let collection = get_vec_map();

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

    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!
    }
}thread2

告诉我们一个道理join只是检测线程有没有运行,不一定是你join的时候它才运行的.
另外就是用好互斥锁Mutex和RefCell:

[*]RefCell是一个纯在单线程环境中的可变,可以配合Rc完成单线程中共享变量的安全,并且可以修改其中成员的值.
[*]Mutex是我们熟悉的互斥锁,支持多线程,和Arc可以配合好
// cow1.rs
//
// This exercise explores the Cow, or Clone-On-Write type. Cow is a
// 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.
//
// This exercise is meant to show you what to expect when passing data to Cow.
// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the
// TODO markers.
//
// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

use std::borrow::Cow;

fn abs_all<'a, 'b>(input: &'a mut Cow<'b, >) -> &'a mut Cow<'b, > {
    for i in 0..input.len() {
      let v = input;
      if v < 0 {
            // Clones into a vector if not already owned.
            input.to_mut() = -v;
      }
    }
    input
}

#
mod tests {
    use super::*;

    #
    fn reference_mutation() -> Result<(), &'static str> {
      // Clone occurs because `input` needs to be mutated.
      let slice = [-1, 0, 1];
      let mut input = Cow::from(&slice[..]);
      match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
      }
    }

    #
    fn reference_no_mutation() -> Result<(), &'static str> {
      // No clone occurs because `input` doesn't need to be mutated.
      let slice = ;
      let mut input = Cow::from(&slice[..]);
      match abs_all(&mut input) {
            Cow::Borrowed(_) => Ok(()),
            _ => Err("Expected owned value"),
      }
    }

    #
    fn owned_no_mutation() -> Result<(), &'static str> {
      // We can also pass `slice` without `&` so Cow owns it directly. In this
      // case no mutation occurs and thus also no clone, but the result is
      // still owned because it was never borrowed or mutated.
      let slice = vec!;
      let mut input = Cow::from(slice);
      match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
      }
    }

    #
    fn owned_mutation() -> Result<(), &'static str> {
      // Of course this is also the case if a mutation does occur. In this
      // case the call to `to_mut()` returns a reference to the same data as
      // before.
      let slice = vec![-1, 0, 1];
      let mut input = Cow::from(slice);
      match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
      }
    }
}log

// threads1.rs
//
// This program spawns multiple threads that each run for at least 250ms, and
// each thread returns how much time they took to complete. The program should
// wait until all the spawned threads have finished and should collect their
// return values into a vector.
//
// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a
// hint.

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

fn main() {
    let mut handles = vec![];
    for i in 0..10 {
      handles.push(thread::spawn(move || {
            let start = Instant::now();
            thread::sleep(Duration::from_millis(250));
            println!("thread {} is complete", i);
            start.elapsed().as_millis()
      }));
    }

    let mut results: Vec<u128> = vec![];
    for handle in handles {
      // TODO: a struct is returned from thread::spawn, can you use it?
      results.push({
            match handle.join(){
                Ok(t) => t,
                Err(_) => panic!("Some thing wrong!!!")
            }
      })
    }

    if results.len() != 10 {
      panic!("Oh no! All the spawned threads did not finish!");
    }

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

好的逻辑是计划出来的是想出来的,不是混出来的,不是尝试出来的,如果这也要以做出来为标准,那么你学了什么呢?
// threads2.rs
//
// 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_completed
//
// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

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

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let mut handles = vec![];
    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
            let mut status_shared= match status_shared.lock()
            {
                Ok(status) => status,
                Err(_) => panic!("Error")
            };
            status_shared.jobs_completed += 1;
      });
      handles.push(handle);
    }
    for handle in handles {
      handle.join().unwrap();
      // TODO: Print the value of the JobStatus.jobs_completed. Did you notice
      // anything interesting in the output? Do you have to 'join' on all the
      // handles?
      let status = status.lock().unwrap();
      println!("jobs completed {}", status.jobs_completed);
    }
}algorithm4


使用 value.cmp 可以防止 T 不支持>, Self {      TreeNode {            value,            left: None,            right: None,      }    }}impl BinarySearchTreewhere    T: Ord,{    fn new() -> Self {      BinarySearchTree { root: None }    }    // Insert a value into the BST    fn insert(&mut self, value: T) {      //TODO      let mut current = &mut self.root;      while let Some(ref mut node) = current {            if value < node.value {                current = &mut node.left;            } else if value > node.value {                current = &mut node.right;            } else {                return;            }      }      *current = Some(Box::new(TreeNode::new(value)));    }    // Search for a value in the BST    fn search(&self, value: T) -> bool {      let mut current = &self.root;      while let Some(node) = current {            match value.cmp(&node.value) {                Ordering::Less => current = &node.left,                Ordering::Greater => current = &node.right,                Ordering::Equal => return true,            }      }      false    }}impl TreeNodewhere    T: Ord,{    // Insert a node into the tree    fn insert(&mut self, value: T) {      if valuepanic!("There is all ready a node left"),                None => self.left = Some(Box::new(TreeNode::::new(value))),            };      }else{            match self.right{                Some(_) => panic!("There is all ready a node right"),                None => self.right = Some(Box::new(TreeNode::::new(value))),            }      }    }    fn is_full(self) -> bool{      (!self.left.is_none()) && (!self.right.is_none())    }}#mod tests {    use super::*;    #    fn test_insert_and_search() {      let mut bst = BinarySearchTree::new();                assert_eq!(bst.search(1), false);                bst.insert(5);      bst.insert(3);      bst.insert(7);      bst.insert(2);      bst.insert(4);                assert_eq!(bst.search(5), true);      assert_eq!(bst.search(3), true);      assert_eq!(bst.search(7), true);      assert_eq!(bst.search(2), true);      assert_eq!(bst.search(4), true);                assert_eq!(bst.search(1), false);      assert_eq!(bst.search(6), false);    }    #    fn test_insert_duplicate() {      let mut bst = BinarySearchTree::new();                bst.insert(1);      bst.insert(1);                assert_eq!(bst.search(1), true);                match bst.root {            Some(ref node) => {                assert!(node.left.is_none());                assert!(node.right.is_none());            },            None => panic!("Root should not be None after insertion"),      }    }}    algorithm6

无比丑陋的DFS.
#TODO 写点好看的吧.
Output:
====================
jobs completed 6
jobs completed 6
jobs completed 6
jobs completed 7
jobs completed 7
jobs completed 7
jobs completed 10
jobs completed 10
jobs completed 10
jobs completed 10

====================不写递归的后果:
GPT写的,多么的聪明
// from_str.rs
//
// This is similar to from_into.rs, but this time we'll implement `FromStr` and
// return errors instead of falling back to a default value. Additionally, upon
// implementing FromStr, you can use the `parse` method on strings to generate
// an object of the implementor type. You can read more about it at
// https://doc.rust-lang.org/std/str/trait.FromStr.html
//
// Execute `rustlings hint from_str` or use the `hint` watch subcommand for a
// hint.

use std::num::ParseIntError;
use std::str::FromStr;

#
struct Person {
    name: String,
    age: usize,
}

// We will use this error type for the `FromStr` implementation.
#
enum ParsePersonError {
    // Empty input string
    Empty,
    // Incorrect number of fields
    BadLen,
    // Empty name field
    NoName,
    // Wrapped error from parse::<usize>()
    ParseInt(ParseIntError),
}

// I AM NOT DONE

// Steps:
// 1. If the length of the provided string is 0, an error should be returned
// 2. Split the given string on the commas present in it
// 3. Only 2 elements should be returned from the split, otherwise return an
//    error
// 4. Extract the first element from the split operation and use it as the name
// 5. Extract the other element from the split operation and parse it into a
//    `usize` as the age with something like `"4".parse::<usize>()`
// 6. If while extracting the name and the age something goes wrong, an error
//    should be returned
// If everything goes well, then return a Result of a Person object
//
// As an aside: `Box<dyn Error>` implements `From<&'_ str>`. This means that if
// you want to return a string error message, you can do so via just using
// return `Err("my error message".into())`.

impl FromStr for Person {
    type Err = ParsePersonError;
    fn from_str(s: &str) -> Result<Person, Self::Err> {
      if s.is_empty() {
            return Err(ParsePersonError::Empty);
      }
      let v:Vec<_> = s.split(",").collect();
      if v.len() != 2{
            return Err(ParsePersonError::BadLen);
      }
      let name = {
            if v.is_empty(){
                return Err(ParsePersonError::NoName);
            }
            v.to_string()
      };
      let age = match v.parse::<usize>(){
            Ok(age) => age,
            Err(err) => return Err(ParsePersonError::ParseInt(err))
      };
      Ok(Person{
            name: name,
            age: age,
      })
    }
}

fn main() {
    let p = "Mark,20".parse::<Person>().unwrap();
    println!("{:?}", p);
}

#
mod tests {
    use super::*;

    #
    fn empty_input() {
      assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty));
    }
    #
    fn good_input() {
      let p = "John,32".parse::<Person>();
      assert!(p.is_ok());
      let p = p.unwrap();
      assert_eq!(p.name, "John");
      assert_eq!(p.age, 32);
    }
    #
    fn missing_age() {
      assert!(matches!(
            "John,".parse::<Person>(),
            Err(ParsePersonError::ParseInt(_))
      ));
    }

    #
    fn invalid_age() {
      assert!(matches!(
            "John,twenty".parse::<Person>(),
            Err(ParsePersonError::ParseInt(_))
      ));
    }

    #
    fn missing_comma_and_age() {
      assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen));
    }

    #
    fn missing_name() {
      assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName));
    }

    #
    fn missing_name_and_age() {
      assert!(matches!(
            ",".parse::<Person>(),
            Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
      ));
    }

    #
    fn missing_name_and_invalid_age() {
      assert!(matches!(
            ",one".parse::<Person>(),
            Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
      ));
    }

    #
    fn trailing_comma() {
      assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen));
    }

    #
    fn trailing_comma_and_some_string() {
      assert_eq!(
            "John,32,man".parse::<Person>(),
            Err(ParsePersonError::BadLen)
      );
    }
}algorithm8

这里出现一个借用性问题: #TODO
//! This is the build script for both tests7 and tests8.
//!
//! You should modify this file to make both exercises pass.


fn main() {
    // In tests7, we should set up an environment variable
    // called `TEST_FOO`. Print in the standard output to let
    // Cargo do it.
    let timestamp = std::time::SystemTime::now()
      .duration_since(std::time::UNIX_EPOCH)
      .unwrap()
      .as_secs(); // What's the use of this timestamp here?
    let your_command = format!(
      "rustc-env=TEST_FOO={}",
      timestamp.to_string()
    );
    println!("cargo:{}", your_command);

    // In tests8, we should enable "pass" feature to make the
    // testcase return early. Fill in the command to tell
    // Cargo about that.
    // let your_command = "Your command here, please checkout exercises/tests/build.rs";
    // println!("cargo:{}", your_command);
}不能被改成:
`
// tests9.rs
//
// Rust is highly capable of sharing FFI interfaces with C/C++ and other statically compiled
// languages, and it can even link within the code itself! It makes it through the extern
// block, just like the code below.
//
// The short string after the `extern` keyword indicates which ABI the externally imported
// function would follow. In this exercise, "Rust" is used, while other variants exists like
// "C" for standard C ABI, "stdcall" for the Windows ABI.
//
// The externally imported functions are declared in the extern blocks, with a semicolon to
// mark the end of signature instead of curly braces. Some attributes can be applied to those
// function declarations to modify the linking behavior, such as # to
// modify the actual symbol names.
//
// If you want to export your symbol to the linking environment, the `extern` keyword can
// also be marked before a function definition with the same ABI string note. The default ABI
// for Rust functions is literally "Rust", so if you want to link against pure Rust functions,
// the whole extern term can be omitted.
//
// Rust mangles symbols by default, just like C++ does. To suppress this behavior and make
// those functions addressable by name, the attribute # can be applied.
//
// In this exercise, your task is to make the testcase able to call the `my_demo_function` in
// module Foo. the `my_demo_function_alias` is an alias for `my_demo_function`, so the two
// line of code in the testcase should call the same function.
//
// You should NOT modify any existing code except for adding two lines of attributes.

// I AM NOT DONE

extern "Rust" {
    #
    fn my_demo_function(a: u32) -> u32;
    #
    #
    fn my_demo_function_alias(a: u32) -> u32;
}

mod Foo {
    // No `extern` equals `extern "Rust"`.
    #
    fn my_demo_function(a: u32) -> u32 {
      a
    }
}

#
mod tests {
    use super::*;

    #
    fn test_success() {
      // The externally imported functions are UNSAFE by default
      // because of untrusted source of other languages. You may
      // wrap them in safe Rust APIs to ease the burden of callers.
      //
      // SAFETY: We know those functions are aliases of a safe
      // Rust function.
      unsafe {
            my_demo_function(123);
            my_demo_function_alias(456);
      }
    }
}/*        queue        This question requires you to use queues to implement the functionality of the stac*/// I AM NOT DONE#pub struct Queue {    elements: Vec,}impl Queue {    pub fn new() -> Queue {      Queue {            elements: Vec::new(),      }    }    pub fn enqueue(&mut self, value: T) {      self.elements.push(value)    }    pub fn dequeue(&mut self) -> Result {      if !self.elements.is_empty() {            Ok(self.elements.remove(0usize))      } else {            Err("Queue is empty")      }    }    pub fn peek(&self) -> Result {      match self.elements.first() {            Some(value) => Ok(value),            None => Err("Queue is empty"),      }    }    pub fn size(&self) -> usize {      self.elements.len()    }    pub fn is_empty(&self) -> bool {      self.elements.is_empty()    }}impl Default for Queue {    fn default() -> Queue {      Queue {            elements: Vec::new(),      }    }}enum SelectedQueue{    Q1,    Q2,}pub struct myStack{        //TODO    flag: SelectedQueue,        q1:Queue,        q2:Queue}impl myStack {    pub fn new() -> Self {      Self {                        //TODO            flag: SelectedQueue::Q1,                        q1:Queue::::new(),                        q2:Queue::::new()      }    }    pub fn push(&mut self, elem: T) {      //TODO      let q = match self.flag{            SelectedQueue::Q1 => &mut self.q1,            SelectedQueue::Q2 => &mut self.q2,      };      q.enqueue(elem);    }    pub fn pop(& mut self) -> Result {      //TODO      let (q1,q2) = match self.flag{            SelectedQueue::Q1 => (&mut self.q1,&mut self.q2),            SelectedQueue::Q2 => (&mut self.q2,&mut self.q1),      };      if q1.is_empty(){            return Err("Stack is empty");      }//! This is the build script for both tests7 and tests8.
//!
//! You should modify this file to make both exercises pass.


fn main() {
    // In tests7, we should set up an environment variable
    // called `TEST_FOO`. Print in the standard output to let
    // Cargo do it.
    let timestamp = std::time::SystemTime::now()
      .duration_since(std::time::UNIX_EPOCH)
      .unwrap()
      .as_secs(); // What's the use of this timestamp here?
    let your_command = format!(
      "rustc-env=TEST_FOO={}",
      timestamp.to_string()
    );
    println!("cargo:{}", your_command);

    // In tests8, we should enable "pass" feature to make the
    // testcase return early. Fill in the command to tell
    // Cargo about that.
    // let your_command = "Your command here, please checkout exercises/tests/build.rs";
    // println!("cargo:{}", your_command);
}      self.flag = match self.flag {            SelectedQueue::Q1 => SelectedQueue::Q2,            SelectedQueue::Q2 => SelectedQueue::Q1,      };      Ok(elem)    }    pub fn is_empty(&self) -> bool {                //TODO      match self.flag{            SelectedQueue::Q1 => self.q1.is_empty(),            SelectedQueue::Q2 => self.q2.is_empty(),      }    }}#mod tests {        use super::*;                #        fn test_queue(){                let mut s = myStack::::new();                assert_eq!(s.pop(), Err("Stack is empty"));      s.push(1);      s.push(2);      s.push(3);      assert_eq!(s.pop(), Ok(3));      assert_eq!(s.pop(), Ok(2));      s.push(4);      s.push(5);      assert_eq!(s.is_empty(), false);      assert_eq!(s.pop(), Ok(5));      assert_eq!(s.pop(), Ok(4));      assert_eq!(s.pop(), Ok(1));      assert_eq!(s.pop(), Err("Stack is empty"));      assert_eq!(s.is_empty(), true);        }}algorithm9

堆的维护,一如既往地尿流到哪,沟就挖到哪.
数据结构堆(Heap)详解-堆的建立、插入、删除、最大堆、最小堆、堆排序等_最大堆 heap 是一个什么样的存在?-CSDN博客
/*
        dfs
        This problem requires you to implement a basic DFS traversal
*/

// I AM NOT DONE
use std::collections::HashSet;
use std::collections::VecDeque;

struct Graph {
    adj: Vec<Vec<usize>>,
}

impl Graph {
    fn new(n: usize) -> Self {
      Graph {
            adj: vec!; n],
      }
    }

    fn add_edge(&mut self, src: usize, dest: usize) {
      self.adj.push(dest);
      self.adj.push(src);
    }

    fn dfs_util(&self, v: usize, visited: &mut HashSet<usize>, visit_order: &mut Vec<usize>) {
      //TODO
      let mut cur = v;// 当前指向的指针
      visit_order.push(cur);
      visited.insert(cur);
      loop{
            let mut cnt = 0;
            for i in &self.adj{
                if !visited.contains(i){
                  visit_order.push(*i);
                  visited.insert(*i);
                  cur = *i;
                  break;
                }
                cnt+=1;
            }
            if cnt>=self.adj.len(){
                if cur==v{
                  break;
                }
                for i in (0..visit_order.len()).rev(){
                  cur = visit_order;
                }
            }
      }
    }

    // Perform a depth-first search on the graph, return the order of visited nodes
    fn dfs(&self, start: usize) -> Vec<usize> {
      let mut visited = HashSet::new();
      let mut visit_order = Vec::new();
      self.dfs_util(start, &mut visited, &mut visit_order);
      visit_order
    }
}

#
mod tests {
    use super::*;

    #
    fn test_dfs_simple() {
      let mut graph = Graph::new(3);
      graph.add_edge(0, 1);
      graph.add_edge(1, 2);

      let visit_order = graph.dfs(0);
      assert_eq!(visit_order, vec!);
    }

    #
    fn test_dfs_with_cycle() {
      let mut graph = Graph::new(4);
      graph.add_edge(0, 1);
      graph.add_edge(0, 2);
      graph.add_edge(1, 2);
      graph.add_edge(2, 3);
      graph.add_edge(3, 3);

      let visit_order = graph.dfs(0);
      assert_eq!(visit_order, vec!);
    }

    #
    fn test_dfs_disconnected_graph() {
      let mut graph = Graph::new(5);
      graph.add_edge(0, 1);
      graph.add_edge(0, 2);
      graph.add_edge(3, 4);

      let visit_order = graph.dfs(0);
      assert_eq!(visit_order, vec!);
      let visit_order_disconnected = graph.dfs(3);
      assert_eq!(visit_order_disconnected, vec!);
    }
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: rustlings 学习随笔