Rust语言圣经(Rust教程 Rust Course和配套练习)
【strings4.rs】
fn string_slice(arg: &str) {
println!("{}", arg);
}
fn string(arg: String) {
println!("{}", arg);
}
fn main() {
string_slice("blue");
string("red".to_string());
string(String::from("hi"));
string("rust is fun!".to_owned());
string("nice weather".into());
string(format!("Interpolation {}", "Station"));
string_slice(&String::from("abc")[0..1]);
string_slice(" hello there ".trim());
string("Happy Monday!".to_string().replace("Mon", "Tues"));
string("mY sHiFt KeY iS sTiCkY".to_lowercase());
}
-
// 构造一个string String::from("hi")
-
// "xxx".to_owned()方法内部掉clone(),将字符串字面量从ROData区复制一份到堆上,并返回堆上数据的所有权;String::from("xxx")内部实际调用的是"xxx".to_owned(); "rust is fun!".to_owned()
-
// 如果可以推断类型使用.into "nice weather".into()
-
// pub fn trim(&self) -> &str // 返回删除了前导和尾随空格的字符串切片。
-
// pub fn to_lowercase(&self) -> String // 返回此字符串切片的小写等效项,作为新的 String
【modules1.rs】
Rust 出于安全的考虑,默认情况下,所有的类型都是私有化的,包括函数、方法、结构体、枚举、常量,是的,就连模块本身也是私有化的。通过pub关键字使得模块内函数可见。
【modules2.rs】
使用 use … as … 关键字可以给模块起别名,注意要给 use 前加 pub 可以使得变成可见的
【modules3.rs】
使用use关键字两个外部模块放在一起
use std::time::{SystemTime , UNIX_EPOCH};
【hashmaps1.rs】
通过 HashMap::new();
创建HashMap,通过basket.insert(String::from("apple"), 3);
方法增加
【hashmaps2.rs】
通过匹配对应的两种水果是否是这两种分别进行判断,如果两种水果是这两种的话,再判断里面的值是否是空,是的话赋值。
- if匹配的时候enum类似要加上前面的类型::
- option的空为None
【hashmaps3.rs】
刚开始获取原始的值,之后分别创建传输的结构体,原始值不为0则调用相加,否则不管。
- 可借用和不可借用之间可以通过调换顺序可以避免报错。
- 注意多看看人家的提示,panic会有相应的解释和对应的行数
【quiz2.rs】
-
Vec类型可以与元组进行复合变成:
input: Vec<(String,Command)>
-
Vec类型必须在中扩展中加入内部存储的类型描述
-
.iter()代表一个迭代器
-
变大写字母:
output.push(string.to_uppercase())
-
去掉前面后面的空格;
output.push(string.trim().to_string())
-
面对不可变引用的可变操作,可以整个变量替代他
Command::Append(i) => { let mut sa = string.to_string(); for n in 0..*i{ sa.push_str("bar"); } output.push(sa.to_string()); }
【options1.rs】
看完TODO
【options2.rs】
-
if let 匹配一个分支
if let Some(word) = optional_word { println!("The word is: {}", word); }
-
while let 只要模式匹配就一直进行
while
循环while let Some(integer) = optional_integers_vec.pop() { println!("current value: {:?}", integer); }
【options3.rs】
不想让Option结束声明周期就用&
match &y {
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
_ => println!("no match"),
}
y; // Fix without deleting this line.
【errors1.rs】
-
assert_eq!
需要左右两个参数的类型相同 -
ok and err,这两个方法可以将Result类型转化为Option类型
pub fn ok(self) -> Option<T> pub fn err(self) -> Option<E>
-
ok_or和ok_or_else,这两个是一组,作用都是从Option转成Result。
pub fn ok_or<E>(self, err: E) -> Result<T, E> pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E,
【errors2.rs】
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>();
let mut f = match qty {
Ok(file) => return Ok(file* cost_per_item + processing_fee),
Err(e) => return Err(e),
};
//Ok(qty * cost_per_item + processing_fee)
}
-
本题是错误传递,要将正确的和错误的都传出来
-
parse:它定义在 std:str 下面,他把这个字符串切片解析成另外一种类型。
【errors3.rs】
- 其实
?
就是一个宏,它的作用跟上面的match
几乎一模一样,但他可以做返回类型Err的隐式转换 - ?操作符只能如下两种样子使用,必须带变量承载值
let v = xxx()?;
xxx()?.yyy()?;
- 主函数也会有返回值,最后必须需要一个返回的东西
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError>{
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>() ? * cost_per_item + processing_fee;
Ok(qty)
}
【errors4.rs】
-
返回的结果是Ok还是Err是我们定的,所以我们可以通过个人定义的逻辑判断判断正误来返回结果
#[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } impl PositiveNonzeroInteger { fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { // Hmm...? Why is this only returning an Ok value? if value > 0{ Ok(PositiveNonzeroInteger(value as u64)) } else if value == 0{ Err(CreationError::Zero) } else { Err(CreationError::Negative) } } } #[test] fn test_creation() { assert!(PositiveNonzeroInteger::new(10).is_ok()); assert_eq!( Err(CreationError::Negative), PositiveNonzeroInteger::new(-10) ); assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); }
【errors5.rs】
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 {}
返回的Err属于实现了某个trait的类型
【errors6.rs】
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
ParseInt(ParseIntError)
}
impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
ParsePosNonzeroError::Creation(err)
}
// TODO: add another error conversion function here.
fn from_parseint(err: ParseIntError) -> ParsePosNonzeroError {
ParsePosNonzeroError::ParseInt(err)
}
}
fn parse_pos_nonzero(s: &str)
-> Result<PositiveNonzeroInteger, ParsePosNonzeroError>
{
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x = s.parse();
let mut f = match x{
Ok(x) => {
return PositiveNonzeroInteger::new(x)
.map_err(ParsePosNonzeroError::from_creation);
},
Err(e) => return Err(ParsePosNonzeroError::ParseInt(e)),
};
}
// 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))
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_error() {
// We can't construct a ParseIntError, so we have to pattern match.
assert!(matches!(
parse_pos_nonzero("not a number"),
Err(ParsePosNonzeroError::ParseInt(_))
));
}
#[test]
fn test_negative() {
assert_eq!(
parse_pos_nonzero("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
);
}
#[test]
fn test_zero() {
assert_eq!(
parse_pos_nonzero("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
);
}
#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42);
assert!(x.is_ok());
assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
}
}
- 多了个错误外层的判断
【generics1.rs】
fn main() {
let mut shopping_list: Vec<String> = Vec::new();
shopping_list.push("milk".to_string());
}
- Vec类型要设定
【generics2.rs】
//struct类型的泛型定义
struct Wrapper <T>{
value: T,
}
//方法的泛型定义
impl <T> Wrapper <T>{
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_u32_in_wrapper() {
assert_eq!(Wrapper::new(42).value, 42);
}
#[test]
fn store_str_in_wrapper() {
assert_eq!(Wrapper::new("Foo").value, "Foo");
}
}
【traits1.rs】
// The trait AppendBar has only one function,
// which appends "Bar" to any object
// implementing this trait.
// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a hint.
trait AppendBar {
fn append_bar(self) -> Self;
}
//实现这个Trait,返回追加的字符串
impl AppendBar for String {
//Add your code here
fn append_bar(self) -> Self{
self+"Bar"
}
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_foo_bar() {
assert_eq!(String::from("Foo").append_bar(), String::from("FooBar"));
}
#[test]
fn is_bar_bar() {
assert_eq!(
String::from("").append_bar().append_bar(),
String::from("BarBar")
);
}
}
【traits2.rs】
trait AppendBar {
fn append_bar(self) -> Self;
}
//TODO: Add your code here
impl AppendBar for Vec<String>{
fn append_bar(mut self) -> Vec<String>{
self.push("Bar".to_string());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_vec_pop_eq_bar() {
let mut foo = vec![String::from("Foo")].append_bar();
assert_eq!(foo.pop().unwrap(), String::from("Bar"));
assert_eq!(foo.pop().unwrap(), String::from("Foo"));
}
}
-
先完成push这个操作,再返回self,要不会报返回类型不匹配的问题
expected struct Vec, found ()
-
传入可变的self才能修改
【traits3.rs】
//具有默认实现的方法其它类型无需再实现该方法,或者也可以选择重载该方法
pub trait Licensed {
fn licensing_info(&self) -> String{
"Some information".to_string()
}
}
struct SomeSoftware {
version_number: i32,
}
struct OtherSoftware {
version_number: String,
}
impl Licensed for SomeSoftware {} // Don't edit this line
impl Licensed for OtherSoftware {} // Don't edit this line
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_licensing_info_the_same() {
let licensing_info = String::from("Some information");
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),
};
assert_eq!(some_software.licensing_info(), licensing_info);
assert_eq!(other_software.licensing_info(), licensing_info);
}
}
【traits4.rs】
pub trait Licensed {
fn licensing_info(&self) -> String {
"some information".to_string()
}
}
struct SomeSoftware {}
struct OtherSoftware {}
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
//两个参数都实现了Licensed这个trait
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.rs】
pub trait SomeTrait {
fn some_function(&self) -> bool {
true
}
}
pub trait OtherTrait {
fn other_function(&self) -> bool {
true
}
}
struct SomeStruct {
name: String,
}
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}
//同一个类型实现了两个trait的约束
fn some_func<T: OtherTrait + SomeTrait>(item: T) -> bool {
item.some_function() && item.other_function()
}
fn main() {}
【quiz3.rs】
pub struct ReportCard<T>{
pub grade: T,
pub student_name: String,
pub student_age: u8,
}
impl <T:std::fmt::Display> ReportCard <T> {
pub fn print(&self) -> String {
format!("{} ({}) - achieved a grade of ",
&self.student_name, &self.student_age) + &self.grade.to_string()
}
}
#[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+"
);
}
}
- 匹配不同的两个类型使用泛型
T:std::fmt::Display
要实现了这种方法的可以进行后续的匹配
【test1.rs】
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert() {
assert!(true,true);
}
}
【test2.rs】
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert_eq() {
assert_eq!(true,true);
}
}
【test3.rs】
pub fn is_even(num: i32) -> bool {
num % 2 == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_true_when_even() {
assert!(is_even(4));
}
//当断言是错误的时候可以使用assert!(! )
#[test]
fn is_false_when_odd() {
assert!(!is_even(5));
}
}
【lifetimes1.rs】
//设定两个的生命周期一样长
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);
}
【lifetimes2.rs】
fn longest (x: String, y: String) -> String {
if &x.len() > &y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
//直接将生命周期传进去
let string2 = String::from("xyz");
result = longest(string1, string2);
}
println!("The longest string is {}", result);
}
【lifetimes3.rs】
//结构体的生命周期表面该传入的str比这个结构体活得长
struct Book <'a>{
author: &'a str,
title: &'a str,
}
fn main() {
let name = String::from("Jill Smith");
let title = String::from("Fish Flying");
let book = Book { author: &name, title: &title };
println!("{} by {}", book.title, book.author);
}
【iterators1.rs】
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
//不断迭代,next 方法返回的是Option类型,没有为None
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
}
【iterators2.rs】
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
//取第一个字符的大写再拼接后面的
Some(first) => {
let up = first.to_string().to_uppercase();
up + &input[1..]
}
}
}
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
// 使用迭代器进行遍历调用
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
let mut numbers: Vec<String> = vec![];
for i in words{
numbers.push(capitalize_first(i));
}
numbers
}
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
let mut S = String::new();
for i in words{
S.push_str(&capitalize_first(&i));
}
S
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
}
【iterators3.rs】
#[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{
let x = Err(DivisionError::DivideByZero);
return x;
}
if a % b == 0{
Ok(a/b)
}
else {
Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: 81,
divisor: 6
}))
}
}
// 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 mut buff:Vec<i32> = vec![];
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
for i in division_results{
match i {
Ok(v) => buff.push(v),
_ => buff.push(0),
}
}
let mut num:Result<Vec<i32>,DivisionError> = Ok(buff);
num
}
// 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 mut num:Vec<Result<i32,DivisionError>> = vec![];
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
for i in division_results{
match i {
Ok(v) => num.push(Ok(v)),
Err(e) => num.push(Err(e)),
}
}
num
}
#[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)]"
);
}
}
- divide
- 首先判断b的类型,如果是0则直接退出
- 后面判断是否可以整除,可以的话就直接返回结果,否则返回定义的错误
- result_with_list
Ok([1, 11, 1426, 3])
从返回的类型判断是Result类型复合Vec- 先获得数组的值,再赋给Result类型进行返回
- list_of_results
[Ok(1), Ok(11), Ok(1426), Ok(3)]
从返回的类型判断是Vec类型复合Result- 直接创建这个Vec然后进行一个个push
【iterators4.rs】
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.
(1u64..num+1).into_iter().fold(1u64, |sum, acm| sum * acm)
}
#[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));
}
}
- 通过range产生连续的数值
- 通过fold函数进行累积
【box1.rs】
#[derive(PartialEq, Debug)]
pub enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
println!("This is an empty cons list: {:?}", create_empty_list());
println!(
"This is a non-empty cons list: {:?}",
create_non_empty_list()
);
}
pub fn create_empty_list() -> List {
let n = List::Nil;
n
}
pub fn create_non_empty_list() -> List {
let n = List::Cons(32,Box::new(List::Nil));
n
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_empty_list() {
assert_eq!(List::Nil, create_empty_list())
}
#[test]
fn test_create_non_empty_list() {
assert_ne!(create_empty_list(), create_non_empty_list())
}
}
- 通过Box可以将内存分配在堆上
- 递归类型可以使用Box< T >使得动态大小类型变成固定大小类型(将
List
存储到堆上,然后使用一个智能指针指向它)。
【arc1.rs】
#![forbid(unused_imports)] // Do not change this, (or the next) line.
use std::sync::Arc;
use std::thread;
fn main() {
let numbers: Vec<_> = (0..100u32).collect();
let shared_numbers = Arc::new(numbers);// TODO
let mut joinhandles = Vec::new();
for offset in 0..8 {
let child_numbers = Arc::clone(&shared_numbers);// TODO
joinhandles.push(thread::spawn(move || {
let sum: u32 = child_numbers.iter().filter(|n| *n % 8 == offset).sum();
println!("Sum of offset {} is {}", offset, sum);
}));
}
for handle in joinhandles.into_iter() {
handle.join().unwrap();
}
}
- 多个线程获得同一个数据,类似于多个指针
【threads2.rs】
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::sync::Mutex;
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 = status.clone();
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 num = status_shared.lock().unwrap();
num.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?
println!("jobs completed {}", Arc::strong_count(&status));
}
}
【threads3.rs】
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
struct Queue {
length: u32,
first_half: Vec<u32>,
second_half: Vec<u32>,
}
impl Queue {
fn new() -> Self {
Queue {
length: 10,
first_half: vec![1, 2, 3, 4, 5],
second_half: vec![6, 7, 8, 9, 10],
}
}
}
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () {
let qc = Arc::new(q);
//由于子线程会拿走发送者的所有权,因此我们必须对发送者进行克隆,然后让每个线程拿走它的一份拷贝
let tx1 = tx.clone();
let qc1 = qc.clone();
let qc2 = qc.clone();
thread::spawn(move || {
for val in &qc1.first_half {
println!("sending {:?}", val);
tx.send(*val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
for val in &qc2.second_half {
println!("sending {:?}", val);
tx1.send(*val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
}
fn main() {
let (tx, rx) = mpsc::channel();
let queue = Queue::new();
let queue_length = queue.length;
send_tx(queue, tx);
let mut total_received: u32 = 0;
for received in rx {
println!("Got: {}", received);
total_received += 1;
}
println!("total numbers received: {}", total_received);
assert_eq!(total_received, queue_length)
}
【macros1.rs】
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
//使用宏的时候加!,定义的时候不用
fn main() {
my_macro!();
}
【macros2.rs】
fn main() {
my_macro!();
}
//#[macro_export] 注释将宏进行了导出,这样其它的包就可以将该宏引入到当前作用域中,然后才能使用。
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
【macros3.rs】
mod macros {
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
fn main() {
my_macro!();
}
【macros4.rs】
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
( $val:expr ) => {
println!("Look at this other macro: {}", $val);
}
}
fn main() {
my_macro!();
my_macro!(7777);
}
【clippy1.rs】
use std::f32;
//使用官方的标准定义
fn main() {
let pi = std::f32::consts::PI;
let radius = 5.00f32;
let area = pi * f32::powi(radius, 2);
println!(
"The area of a circle with radius {:.2} is {:.5}!",
radius, area
)
}
【clippy2.rs】
fn main() {
let mut res = 42;
let option = Some(12);
if let Some(x) = option {
res += x;
}
println!("{}", res);
}
【clippy3.rs】
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
my_option.unwrap() ;
let my_arr = &[
-1, -2, -3,
-4, -5, -6
];
println!("My array! Here it is: {:?}", my_arr);
let my_empty_vec = vec![0, 0, 0, 0, 0];
println!("This Vec is empty, see? {:?}", my_empty_vec);
let mut value_a = 45;
let mut value_b = 66;
// Let's swap these two!
// value_a = value_b;
// value_b = value_a;
std::mem::swap(&mut value_a, &mut value_b);
println!("value a: {}; value b: {}", value_a, value_b);
}
【using_as.rs】
fn average(values: &[f64]) -> f64 {
let total = values.iter().sum::<f64>();
//转换成同类型的值执行
total / values.len() as f64
}
fn main() {
let values = [3.5, 0.3, 13.0, 11.7];
println!("{}", average(&values));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_proper_type_and_value() {
assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125);
}
}
【form_into.rs】
#[derive(Debug)]
struct Person {
name: String,
age: usize,
}
// We implement the Default trait to use it as a fallback
// when the provided string is not convertible into a Person object
impl Default for Person {
fn default() -> Person {
Person {
name: String::from("John"),
age: 30,
}
}
}
// Your task is to complete this implementation
// in order for the line `let p = Person::from("Mark,20")` to compile
// Please note that you'll need to parse the age component into a `usize`
// with something like `"4".parse::<usize>()`. The outcome of this needs to
// be handled appropriately.
//
// Steps:
// 1. If the length of the provided string is 0, then return the default of Person
// 2. Split the given string on the commas present in it
// 3. Extract the first element from the split operation and use it as the name
// 4. If the name is empty, then return the default of Person
// 5. Extract the other element from the split operation and parse it into a `usize` as the age
// If while parsing the age, something goes wrong, then return the default of Person
// Otherwise, then return an instantiated Person object with the results
// I AM NOT DONE
impl From<&str> for Person {
fn from(s: &str) -> Person {
let mut x:Person = Person::default();
let mut n:Person = Person::default();
//空值判断
if s == ""{
return n;
}
//,判断
let mut num = 0;
for i in s.chars() {
if i == ','{
num +=1;
}
}
if num != 1{
return n;
}
//分割
let v: Vec<&str> = s.split(",").collect();
if (&v[0]).to_string() != ""{
x.name = (&v[0]).to_string();
}
else {
return n;
}
match v[1].parse::<usize>(){
Ok(num) => x.age = num,
Err(e) => return n,
}
x
}
}
fn main() {
// Use the `from` function
let p1 = Person::from("Mark,20");
// Since From is implemented for Person, we should be able to use Into
let p2: Person = "Gerald,70".into();
println!("{:?}", p1);
println!("{:?}", p2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
// Test that the default person is 30 year old John
let dp = Person::default();
assert_eq!(dp.name, "John");
assert_eq!(dp.age, 30);
}
#[test]
fn test_bad_convert() {
// Test that John is returned when bad string is provided
let p = Person::from("");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_good_convert() {
// Test that "Mark,20" works
let p = Person::from("Mark,20");
assert_eq!(p.name, "Mark");
assert_eq!(p.age, 20);
}
#[test]
fn test_bad_age() {
// Test that "Mark,twenty" will return the default person due to an error in parsing age
let p = Person::from("Mark,twenty");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_comma_and_age() {
let p: Person = Person::from("Mark");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_age() {
let p: Person = Person::from("Mark,");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name() {
let p: Person = Person::from(",1");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name_and_age() {
let p: Person = Person::from(",");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name_and_invalid_age() {
let p: Person = Person::from(",one");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_trailing_comma() {
let p: Person = Person::from("Mike,32,");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_trailing_comma_and_some_string() {
let p: Person = Person::from("Mike,32,man");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
}
【from_str.rs】
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> {
let mut x:Person = Person {
name: String::from("John"),
age: 30,
};
let mut n:Person = Person {
name: String::from("John"),
age: 30,
};
//空值判断
if s == ""{
return Err(ParsePersonError::Empty);
}
//,判断
let mut num = 0;
for i in s.chars() {
if i == ','{
num +=1;
}
}
if num != 1{
return Err(ParsePersonError::BadLen);
}
//分割
let v: Vec<&str> = s.split(",").collect();
if (&v[0]).to_string() != ""{
x.name = (&v[0]).to_string();
}
else {
return Err(ParsePersonError::NoName);
}
match v[1].parse::<usize>(){
Ok(num) => x.age = num,
Err(e) => return Err(ParsePersonError::ParseInt(e)) ,
}
Ok(x)
}
}
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)
);
}
}
【try_from_into.rs】
use std::convert::{TryFrom, TryInto};
#[derive(Debug, PartialEq)]
struct Color {
red: u8,
green: u8,
blue: u8,
}
// We will use this error type for these `TryFrom` conversions.
#[derive(Debug, PartialEq)]
enum IntoColorError {
// Incorrect length of slice
BadLen,
// Integer conversion error
IntConversion,
}
// I AM NOT DONE
// Your task is to complete this implementation
// and return an Ok result of inner type Color.
// You need to create an implementation for a tuple of three integers,
// an array of three integers, and a slice of integers.
//
// Note that the implementation for tuple and array will be checked at compile time,
// but the slice implementation needs to check the slice length!
// Also note that correct RGB color values must be integers in the 0..=255 range.
// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
type Error = IntoColorError;
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
let mut c = Color { red: 0, green: 0, blue: 0 };
if tuple.0 < 0 || tuple.0>255{
return Err(IntoColorError::IntConversion);
}
else{
c.red = tuple.0 as u8;
}
if tuple.1 < 0 || tuple.1>255{
return Err(IntoColorError::IntConversion);
}
else{
c.green = tuple.1 as u8;
}
if tuple.2 < 0 || tuple.2>255{
return Err(IntoColorError::IntConversion);
}
else{
c.blue = tuple.2 as u8;
}
Ok(c)
}
}
// Array implementation
impl TryFrom<[i16; 3]> for Color {
type Error = IntoColorError;
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
let mut c = Color { red: 0, green: 0, blue: 0 };
if arr[0] < 0 || arr[0]>255{
return Err(IntoColorError::IntConversion);
}
else{
c.red = arr[0] as u8;
}
if arr[1] < 0 || arr[1]>255 {
return Err(IntoColorError::IntConversion);
}
else{
c.green = arr[1] as u8;
}
if arr[2] < 0 || arr[2]>255{
return Err(IntoColorError::IntConversion);
}
else{
c.blue = arr[2] as u8;
}
Ok(c)
}
}
// Slice implementation
impl TryFrom<&[i16]> for Color {
type Error = IntoColorError;
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
let mut c = Color { red: 0, green: 0, blue: 0 };
if slice.len() != 3{
return Err(IntoColorError::BadLen);
}
if slice[0] < 0 || slice[0]>255{
return Err(IntoColorError::IntConversion);
}
else{
c.red = slice[0] as u8;
}
if slice[1] < 0 || slice[1]>255{
return Err(IntoColorError::IntConversion);
}
else{
c.green = slice[1] as u8;
}
if slice[2] < 0 || slice[2]>255{
return Err(IntoColorError::IntConversion);
}
else{
c.blue = slice[2] as u8;
}
Ok(c)
}
}
fn main() {
// Use the `try_from` function
let c1 = Color::try_from((183, 65, 14));
println!("{:?}", c1);
// Since TryFrom is implemented for Color, we should be able to use TryInto
let c2: Result<Color, _> = [183, 65, 14].try_into();
println!("{:?}", c2);
let v = vec![183, 65, 14];
// With slice we should use `try_from` function
let c3 = Color::try_from(&v[..]);
println!("{:?}", c3);
// or take slice within round brackets and use TryInto
let c4: Result<Color, _> = (&v[..]).try_into();
println!("{:?}", c4);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tuple_out_of_range_positive() {
assert_eq!(
Color::try_from((256, 1000, 10000)),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_tuple_out_of_range_negative() {
assert_eq!(
Color::try_from((-1, -10, -256)),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_tuple_sum() {
assert_eq!(
Color::try_from((-1, 255, 255)),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_tuple_correct() {
let c: Result<Color, _> = (183, 65, 14).try_into();
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_array_out_of_range_positive() {
let c: Result<Color, _> = [1000, 10000, 256].try_into();
assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_out_of_range_negative() {
let c: Result<Color, _> = [-10, -256, -1].try_into();
assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_sum() {
let c: Result<Color, _> = [-1, 255, 255].try_into();
assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_correct() {
let c: Result<Color, _> = [183, 65, 14].try_into();
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_slice_out_of_range_positive() {
let arr = [10000, 256, 1000];
assert_eq!(
Color::try_from(&arr[..]),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_slice_out_of_range_negative() {
let arr = [-256, -1, -10];
assert_eq!(
Color::try_from(&arr[..]),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_slice_sum() {
let arr = [-1, 255, 255];
assert_eq!(
Color::try_from(&arr[..]),
Err(IntoColorError::IntConversion)
);
}
#[test]
fn test_slice_correct() {
let v = vec![183, 65, 14];
let c: Result<Color, _> = Color::try_from(&v[..]);
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_slice_excess_length() {
let v = vec![0, 0, 0, 0];
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
}
#[test]
fn test_slice_insufficient_length() {
let v = vec![0, 0];
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
}
}
【as_ref_mut.rs】
// Obtain the number of bytes (not characters) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn byte_counter<T:AsRef<str>>(arg: T) -> usize {
arg.as_ref().as_bytes().len()
}
// Obtain the number of characters (not bytes) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn char_counter<T:AsRef<str>>(arg: T) -> usize {
arg.as_ref().chars().count()
}
// Squares a number using AsMut. Add the trait bound as is appropriate and
// implement the function body.
fn num_sq<T:AsMut<u32>>(arg: &mut T) {
let num = *arg.as_mut() * *arg.as_mut();
*arg.as_mut() = num;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn different_counts() {
let s = "Café au lait";
assert_ne!(char_counter(s), byte_counter(s));
}
#[test]
fn same_counts() {
let s = "Cafe au lait";
assert_eq!(char_counter(s), byte_counter(s));
}
#[test]
fn different_counts_using_string() {
let s = String::from("Café au lait");
assert_ne!(char_counter(s.clone()), byte_counter(s));
}
#[test]
fn same_counts_using_string() {
let s = String::from("Cafe au lait");
assert_eq!(char_counter(s.clone()), byte_counter(s));
}
#[test]
fn mult_box() {
let mut num: Box<u32> = Box::new(3);
num_sq(&mut num);
assert_eq!(*num, 9);
}
}
🎉 All exercises completed! 🎉
+----------------------------------------------------+
| You made it to the Fe-nish line! |
+-------------------------- ------------------------+
\\/
▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒
▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒
▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓
▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒
▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒
▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒
▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒
▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒
▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒
▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒
▒▒ ▒▒ ▒▒ ▒▒