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
#[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<T>
,而当初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.
#[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
的特征.
特征对象指向实现了
Draw
特征的类型的实例,也就是指向了Button
或者SelectBox
的实例,这种映射关系是存储在一张表中,可以在运行时通过特征对象找到具体调用的类型方法。
可以通过&
引用或者Box<T>
智能指针的方式来创建特征对象。
那么Box<dyn Trait>
是一种创建特征对象的方式.
对于Box<T>
智能指针本身,有解释,我的理解暂时是在堆上创建一个对象,可以看作包裹的内容是保存在堆上的对象的一个引用.
traits4
这里参考了:捋捋 Rust 中的 impl Trait 和 dyn Trait - 知乎 (zhihu.com)
由于输入的时候不能输入Box
包裹,可以直接用impl Trait
而不是Box<dyn Trait>
.
但是返回值不能是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);
}
}
iterators1
- 如果一个迭代器没有
next
那么会返回一个None
- 三种获取迭代器的方法的不同:
iter()
获取一个引用,因此这里做比较的时候也是和Some(&"banana")
引用进行比较into_iter()
则是直接把所有权交了,原来的值的所有权就给了这边这个迭代器对象了iter_mut()
则是允许获取一个可变引用,这样就可以修改里边的内容
// 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
}
iterator3
这里学到了:
map
是惰性的,返回的还是一个迭代器collect
是灵活的,可以通过collect::<>
指定输出类型,甚至可以贴心地把Vec<Result<i32, DivisionError>>
给输出成Result<Vec<i32>,DivisionError>
// 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)]"
);
}
}
iterators4
区间表达式 - Rust 参考手册 中文版 (rustwiki.org)
// 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));
}
}
iterator5
这里存在一个认知问题,就是还是存在对:
- 引用和自动解引用和
*
for
的语法糖- 借用
不熟悉
// 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]
}
}
cow1
这里如果像我一样很难理解高级语言,那么我们可以去看它的实现.
这里Cow
实现了两个Trait
,而且是在泛型的情况下为专门的类型适配了专门的特性.
这里这两个from
就引起了我的思考:
&[T]
和[T]
之间有什么引用和引用的目标之间的自动转换.- 尤其是
println!
输出多层引用的时候误导了我. - Deref 解引用使得这种误导更加严重,Rust中的 实现了
Deref
的类型.
- 尤其是
Cow
的from
是根据类型实现的泛型.
根据这一节所学,可以看到本来就是在讨论Cow
在面对引用和引用的目标时候有不同的表现,因此应该不是自动进行了解引用:
这里看源码看了半天找不到的原因是没有弄明白&[T]
,[T]
,Vec<T>
,vec!
的区别.
- 首先
[T]
是数组Vec<T>
是可变数组,是不同的. &[T]
是一个数组的引用.vec!
是一个宏,返回的是Vec<T>
.
这里必须提到,Cow
没有实现对于[T]
的from
,所以其中使用的是vec!
返回的Vec<T>
.
例子中,一会使用&[T]
一会使用Vec<T>
给了我非常大的误导
这里还有一个遗漏的点,数组切片.
let mut input = Cow::from(&slice[..]);
这一句用的就是数组切片.
// 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"),
}
}
}
threads1
如果大家学过其它语言的多线程,可能就知道不同语言对于线程的实现可能大相径庭:
- 由于操作系统提供了创建线程的 API,因此部分语言会直接调用该 API 来创建线程,因此最终程序内的线程数和该程序占用的操作系统线程数相等,一般称之为1:1 线程模型,例如 Rust。
- 还有些语言在内部实现了自己的线程模型(绿色线程、协程),程序内部的 M 个线程最后会以某种映射方式使用 N 个操作系统线程去运行,因此称之为M:N 线程模型,其中 M 和 N 并没有特定的彼此限制关系。一个典型的代表就是 Go 语言。
- 还有些语言使用了 Actor 模型,基于消息传递进行并发,例如 Erlang 语言。
总之,每一种模型都有其优缺点及选择上的权衡,而 Rust 在设计时考虑的权衡就是运行时(Runtime)。出于 Rust 的系统级使用场景,且要保证调用 C 时的极致性能,它最终选择了尽量小的运行时实现。
有时候只看一本书是不够的,还需要多看几本书.
// 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);
}
}
thread2
告诉我们一个道理join
只是检测线程有没有运行,不一定是你join
的时候它才运行的.
另外就是用好互斥锁Mutex
和RefCell
:
RefCell
是一个纯在单线程环境中的可变,可以配合Rc
完成单线程中共享变量的安全,并且可以修改其中成员的值.Mutex
是我们熟悉的互斥锁,支持多线程,和Arc
可以配合好
// 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);
}
}
log
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
====================
from_str
好的逻辑是计划出来的是想出来的,不是混出来的,不是尝试出来的,如果这也要以做出来为标准,那么你学了什么呢?
// 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)
);
}
}
test7&test8
最令人作呕的一集,这个build.rs
会互相影响,也就是如果你不去动(1. 注释掉 2. 修改好)test8
的代码,你没办法过test7
.
//! 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);
}
test9
这里#[no_mangle]
用于告诉 Rust 编译器:不要乱改函数的名称.
Mangling
的定义是:当 Rust 因为编译需要去修改函数的名称,例如为了让名称包含更多的信息,这样其它的编译部分就能从该名称获取相应的信息,这种修改会导致函数名变得相当不可读。
因此,为了让 Rust 函数能顺利被其它语言调用,我们必须要禁止掉该功能。
这里#[link_name = "my_demo_function"]
规定了my_demo_function_alias
在链接的时候也去找my_demo_function
,这个找是链接过程,比如C中的函数,如果声明了但是没实现则先空着等链接的时候解决:
在 Rust 中,
#[link_name = "my_demo_function"]
是一个属性(attribute),它用于指定链接器应该使用的符号名称。当你在 Rust 代码中声明一个外部函数时,可以使用这个属性来告诉编译器和链接器该函数在链接时实际的符号名是什么。
// 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);
}
}
}
algorithm4
使用 value.cmp
可以防止 T
不支持>
,<
的情况
/*
binary_search tree
This problem requires you to implement a basic interface for a binary tree
*/
//I AM NOT DONE
use std::cmp::Ordering;
use std::fmt::Debug;
#[derive(Debug)]
struct TreeNode<T>
where
T: Ord,
{
value: T,
left: Option<Box<TreeNode<T>>>,
right: Option<Box<TreeNode<T>>>,
}
#[derive(Debug)]
struct BinarySearchTree<T>
where
T: Ord,
{
root: Option<Box<TreeNode<T>>>,
}
impl<T> TreeNode<T>
where
T: Ord,
{
fn new(value: T) -> Self {
TreeNode {
value,
left: None,
right: None,
}
}
}
impl<T> BinarySearchTree<T>
where
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<T> TreeNode<T>
where
T: Ord,
{
// Insert a node into the tree
fn insert(&mut self, value: T) {
if value <= self.value {
match self.left{
Some(_) => panic!("There is all ready a node left"),
None => self.left = Some(Box::new(TreeNode::<T>::new(value))),
};
}else{
match self.right{
Some(_) => panic!("There is all ready a node right"),
None => self.right = Some(Box::new(TreeNode::<T>::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"),
}
}
}
algorithm6
无比丑陋的DFS
.
#TODO
写点好看的吧.
/*
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]);
}
}
不写递归的后果:
GPT写的,多么的聪明
// 辅助函数:执行深度优先搜索
fn dfs_util(&self, v: usize, visited: &mut HashSet<usize>, visit_order: &mut Vec<usize>) {
// 标记当前节点为已访问
visited.insert(v);
// 将当前节点加入访问顺序列表
visit_order.push(v);
// 递归访问所有未访问的邻接节点
for &neighbor in &self.adj[v] {
if !visited.contains(&neighbor) {
self.dfs_util(neighbor, visited, visit_order);
}
}
}
algorithm8
这里出现一个借用性问题: #TODO
let mut elem = q1.dequeue().unwrap();
while !q1.is_empty(){
q2.enqueue(elem);
elem = q1.dequeue().unwrap();
}
不能被改成:
`
let mut elem = q1.dequeue()?;
while !q1.is_empty(){
q2.enqueue(elem);
elem = q1.dequeue()?;
}
/*
queue
This question requires you to use queues to implement the functionality of the stac
*/
// I AM NOT DONE
#[derive(Debug)]
pub struct Queue<T> {
elements: Vec<T>,
}
impl<T> Queue<T> {
pub fn new() -> Queue<T> {
Queue {
elements: Vec::new(),
}
}
pub fn enqueue(&mut self, value: T) {
self.elements.push(value)
}
pub fn dequeue(&mut self) -> Result<T, &str> {
if !self.elements.is_empty() {
Ok(self.elements.remove(0usize))
} else {
Err("Queue is empty")
}
}
pub fn peek(&self) -> Result<&T, &str> {
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<T> Default for Queue<T> {
fn default() -> Queue<T> {
Queue {
elements: Vec::new(),
}
}
}
enum SelectedQueue{
Q1,
Q2,
}
pub struct myStack<T>
{
//TODO
flag: SelectedQueue,
q1:Queue<T>,
q2:Queue<T>
}
impl<T> myStack<T> {
pub fn new() -> Self {
Self {
//TODO
flag: SelectedQueue::Q1,
q1:Queue::<T>::new(),
q2:Queue::<T>::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<T, &str> {
//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");
}
let mut elem = q1.dequeue().unwrap();
while !q1.is_empty(){
q2.enqueue(elem);
elem = q1.dequeue().unwrap();
}
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::<i32>::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博客
/*
heap
This question requires you to implement a binary heap function
*/
// I AM NOT DONE
use std::cmp::Ord;
use std::default::Default;
pub struct Heap<T>
where
T: Default,
{
count: usize,
items: Vec<T>,
comparator: fn(&T, &T) -> bool,
}
impl<T> Heap<T>
where
T: Default,
{
pub fn new(comparator: fn(&T, &T) -> bool) -> Self {
Self {
count: 0,
items: vec![T::default()],
comparator,
}
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn add(&mut self, value: T) {
//TODO
self.count += 1;
self.items.push(value);
let mut idx = self.count;
while idx > 1 && (self.comparator)(&self.items[idx], &self.items[self.parent_idx(idx)]) {
let parent_idx = self.parent_idx(idx);
self.items.swap(idx, parent_idx);
idx = self.parent_idx(idx);
}
}
fn parent_idx(&self, idx: usize) -> usize {
idx / 2
}
fn children_present(&self, idx: usize) -> bool {
self.left_child_idx(idx) <= self.count
}
fn left_child_idx(&self, idx: usize) -> usize {
idx * 2
}
fn right_child_idx(&self, idx: usize) -> usize {
self.left_child_idx(idx) + 1
}
fn smallest_child_idx(&self, idx: usize) -> usize {
//TODO
if !self.children_present(idx) {
return 0;
}
let left = self.left_child_idx(idx);
let right = self.right_child_idx(idx);
if right <= self.count && (self.comparator)(&self.items[right], &self.items[left]) {
right
} else {
left
}
}
}
impl<T> Heap<T>
where
T: Default + Ord,
{
/// Create a new MinHeap
pub fn new_min() -> Self {
Self::new(|a, b| a < b)
}
/// Create a new MaxHeap
pub fn new_max() -> Self {
Self::new(|a, b| a > b)
}
}
impl<T> Iterator for Heap<T>
where
T: Default,
{
type Item = T;
fn next(&mut self) -> Option<T> {
//TODO
if self.is_empty() {
return None;
}
self.items.swap(1, self.count);
let r = self.items.pop();
self.count -= 1;
let mut idx = 1;
while self.children_present(idx){
let left = self.left_child_idx(idx);
let right= self.right_child_idx(idx);
if right > self.count {
if (self.comparator)(&self.items[left], &self.items[idx]) {
self.items.swap(idx, left);
idx = left;
}
break;
}
if (self.comparator)(&self.items[left], &self.items[right]) {
if (self.comparator)(&self.items[left], &self.items[idx]) {
self.items.swap(idx, left);
idx = left;
}
} else {
if (self.comparator)(&self.items[right], &self.items[idx]) {
self.items.swap(idx, right);
idx = right;
}
}
}
r
}
}
pub struct MinHeap;
impl MinHeap {
#[allow(clippy::new_ret_no_self)]
pub fn new<T>() -> Heap<T>
where
T: Default + Ord,
{
Heap::new(|a, b| a < b)
}
}
pub struct MaxHeap;
impl MaxHeap {
#[allow(clippy::new_ret_no_self)]
pub fn new<T>() -> Heap<T>
where
T: Default + Ord,
{
Heap::new(|a, b| a > b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_heap() {
let mut heap = MaxHeap::new::<i32>();
assert_eq!(heap.next(), None);
}
#[test]
fn test_min_heap() {
let mut heap = MinHeap::new();
heap.add(4);
heap.add(2);
heap.add(9);
heap.add(11);
assert_eq!(heap.len(), 4);
assert_eq!(heap.next(), Some(2));
assert_eq!(heap.next(), Some(4));
assert_eq!(heap.next(), Some(9));
heap.add(1);
assert_eq!(heap.next(), Some(1));
}
#[test]
fn test_max_heap() {
let mut heap = MaxHeap::new();
heap.add(4);
heap.add(2);
heap.add(9);
heap.add(11);
assert_eq!(heap.len(), 4);
assert_eq!(heap.next(), Some(11));
assert_eq!(heap.next(), Some(9));
assert_eq!(heap.next(), Some(4));
heap.add(1);
assert_eq!(heap.next(), Some(2));
}
}