序言
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
- #[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,
- }
- }
- #[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);
- }
- }
复制代码 这里注意这个,这里有一个结构体更新语法的问题:- 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[0].to_string();
- let team_1_score: u8 = v[2].parse().unwrap();
- let team_2_name = v[1].to_string();
- let team_2_score: u8 = v[3].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
- }
- #[cfg(test)]
- 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
- }
- #[test]
- 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"]
- );
- }
- #[test]
- 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);
- }
- #[test]
- 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
- #[cfg(test)]
- mod tests {
- #[test]
- 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);
- }
- }
- #[test]
- fn layered_option() {
- let range = 10;
- let mut optional_integers: Vec<Option<i8>> = vec![None];
- 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:
ebug 的实现,这使得你可以使用 {:?} 或 {:#?} 格式说明符来打印该类型的实例。
- 这对于调试非常有用,因为它可以帮助你查看结构体或枚举实例的内容。
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.
- #[derive(PartialEq, Debug)]
- struct PositiveNonzeroInteger(u64);
- #[derive(PartialEq, Debug)]
- 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()
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn compare_license_information() {
- let some_software = SomeSoftware {};
- let other_software = OtherSoftware {};
- assert!(compare_license_types(some_software, other_software));
- }
- #[test]
- 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)
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- 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"
- );
- }
- #[test]
- 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}
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- 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
- }
- #[test]
- 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);
- }
- #[test]
- 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
这里存在一个认知问题,就是还是存在对:
不熟悉- // 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
- #[derive(Debug, PartialEq, Eq)]
- pub enum DivisionError {
- NotDivisible(NotDivisibleError),
- DivideByZero,
- }
- #[derive(Debug, PartialEq, Eq)]
- 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([1, 11, 1426, 3])
- fn result_with_list() -> Result<Vec<i32>,DivisionError> {
- let numbers = vec![27, 297, 38502, 81];
- 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: [Ok(1), Ok(11), Ok(1426), Ok(3)]
- fn list_of_results() -> Vec<Result<i32, DivisionError>> {
- let numbers = vec![27, 297, 38502, 81];
- let division_results = numbers.into_iter().map(|n| divide(n, 27));
- division_results.collect::<Vec<Result<i32, DivisionError>>>()
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn test_success() {
- assert_eq!(divide(81, 9), Ok(9));
- }
- #[test]
- fn test_not_divisible() {
- assert_eq!(
- divide(81, 6),
- Err(DivisionError::NotDivisible(NotDivisibleError {
- dividend: 81,
- divisor: 6
- }))
- );
- }
- #[test]
- fn test_divide_by_0() {
- assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
- }
- #[test]
- fn test_divide_0_by_something() {
- assert_eq!(divide(0, 81), Ok(0));
- }
- #[test]
- fn test_result_with_list() {
- assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])");
- }
- #[test]
- fn test_list_of_results() {
- assert_eq!(
- format!("{:?}", list_of_results()),
- "[Ok(1), Ok(11), Ok(1426), Ok(3)]"
- );
- }
- }
复制代码 cow1
这里如果像我一样很难理解高级语言,那么我们可以去看它的实现.
这里Cow实现了两个Trait,而且是在泛型的情况下为专门的类型适配了专门的特性.
这里这两个from就引起了我的思考:
- &[T]和[T]之间有什么引用和引用的目标之间的自动转换.
- 尤其是println!输出多层引用的时候误导了我.
- Deref 解引用使得这种误导更加严重,Rust中的 实现了Deref 的类型.
- Cow的from是根据类型实现的泛型.
根据这一节所学,可以看到本来就是在讨论Cow在面对引用和引用的目标时候有不同的表现,因此应该不是自动进行了解引用:
- Cow对&[T]的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.
这里看源码看了半天找不到的原因是没有弄明白&[T],[T],Vec,vec!的区别.
- 首先[T]是数组Vec是可变数组,是不同的.
- &[T]是一个数组的引用.
- vec!是一个宏,返回的是Vec.
这里必须提到,Cow没有实现对于[T]的from,所以其中使用的是vec!返回的Vec.
例子中,一会使用&[T]一会使用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()
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn factorial_of_0() {
- assert_eq!(1, factorial(0));
- }
- #[test]
- fn factorial_of_1() {
- assert_eq!(1, factorial(1));
- }
- #[test]
- fn factorial_of_2() {
- assert_eq!(2, factorial(2));
- }
- #[test]
- 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;
- #[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
- }
- 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: &[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
- }
- fn count_collection_iterator(collection: &[HashMap<String, Progress>], 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()
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn count_complete() {
- let map = get_map();
- assert_eq!(3, count_iterator(&map, Progress::Complete));
- }
- #[test]
- fn count_some() {
- let map = get_map();
- assert_eq!(1, count_iterator(&map, Progress::Some));
- }
- #[test]
- fn count_none() {
- let map = get_map();
- assert_eq!(2, count_iterator(&map, Progress::None));
- }
- #[test]
- fn count_complete_equals_for() {
- let map = get_map();
- let progress_states = vec![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!(
- 6,
- count_collection_iterator(&collection, Progress::Complete)
- );
- }
- #[test]
- fn count_collection_some() {
- let collection = get_vec_map();
- assert_eq!(1, count_collection_iterator(&collection, Progress::Some));
- }
- #[test]
- fn count_collection_none() {
- let collection = get_vec_map();
- assert_eq!(4, count_collection_iterator(&collection, Progress::None));
- }
- #[test]
- fn count_collection_equals_for() {
- let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
- 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![map, other]
- }
- }
复制代码 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, [i32]>) -> &'a mut Cow<'b, [i32]> {
- for i in 0..input.len() {
- let v = input[i];
- if v < 0 {
- // Clones into a vector if not already owned.
- input.to_mut()[i] = -v;
- }
- }
- input
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- 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"),
- }
- }
- #[test]
- fn reference_no_mutation() -> Result<(), &'static str> {
- // No clone occurs because `input` doesn't need to be mutated.
- let slice = [0, 1, 2];
- let mut input = Cow::from(&slice[..]);
- match abs_all(&mut input) {
- Cow::Borrowed(_) => Ok(()),
- _ => Err("Expected owned value"),
- }
- }
- #[test]
- 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![0, 1, 2];
- let mut input = Cow::from(slice);
- match abs_all(&mut input) {
- Cow::Owned(_) => Ok(()),
- _ => Err("Expected owned value"),
- }
- }
- #[test]
- 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: ess => 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 value panic!("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()) }}#[cfg(test)]mod tests { use super::*; #[test] 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); } #[test] 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"), } }} [/code]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;
- #[derive(Debug, PartialEq)]
- struct Person {
- name: String,
- age: usize,
- }
- // We will use this error type for the `FromStr` implementation.
- #[derive(Debug, PartialEq)]
- 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[0].is_empty(){
- return Err(ParsePersonError::NoName);
- }
- v[0].to_string()
- };
- let age = match v[1].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);
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn empty_input() {
- assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty));
- }
- #[test]
- 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);
- }
- #[test]
- fn missing_age() {
- assert!(matches!(
- "John,".parse::<Person>(),
- Err(ParsePersonError::ParseInt(_))
- ));
- }
- #[test]
- fn invalid_age() {
- assert!(matches!(
- "John,twenty".parse::<Person>(),
- Err(ParsePersonError::ParseInt(_))
- ));
- }
- #[test]
- fn missing_comma_and_age() {
- assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen));
- }
- #[test]
- fn missing_name() {
- assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName));
- }
- #[test]
- fn missing_name_and_age() {
- assert!(matches!(
- ",".parse::<Person>(),
- Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
- ));
- }
- #[test]
- fn missing_name_and_invalid_age() {
- assert!(matches!(
- ",one".parse::<Person>(),
- Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
- ));
- }
- #[test]
- fn trailing_comma() {
- assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen));
- }
- #[test]
- 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 #[link_name = ".."] 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 #[no_mangle] 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" {
- #[no_mangle]
- fn my_demo_function(a: u32) -> u32;
- #[no_mangle]
- #[link_name = "my_demo_function"]
- fn my_demo_function_alias(a: u32) -> u32;
- }
- mod Foo {
- // No `extern` equals `extern "Rust"`.
- #[no_mangle]
- fn my_demo_function(a: u32) -> u32 {
- a
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- 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#[derive(Debug)]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(), } }}#[cfg(test)]mod tests { use super::*; #[test] 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![vec![]; n],
- }
- }
- fn add_edge(&mut self, src: usize, dest: usize) {
- self.adj[src].push(dest);
- self.adj[dest].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[cur]{
- if !visited.contains(i){
- visit_order.push(*i);
- visited.insert(*i);
- cur = *i;
- break;
- }
- cnt+=1;
- }
- if cnt>=self.adj[cur].len(){
- if cur==v{
- break;
- }
- for i in (0..visit_order.len()).rev(){
- cur = visit_order[i];
- }
- }
- }
- }
- // 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
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- 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![0, 1, 2]);
- }
- #[test]
- 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![0, 1, 2, 3]);
- }
- #[test]
- 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![0, 1, 2]);
- let visit_order_disconnected = graph.dfs(3);
- assert_eq!(visit_order_disconnected, vec![3, 4]);
- }
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |