系列综述:
目的:本篇文章是个人通过Rustlings学习Rust过程中整理的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
来源:材料主要源于Rustlings
进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
结语:如果有帮到你的地方,就点个赞和关注一下呗,谢谢!!!
点此到文末惊喜↩︎
rustlings watch
命令,进行自动检测编译模式// I AM NOT DONE
// 打印函数
println!("Hello world");
答案
println!("Hello {}!", "World");
知识点
println!
是一个宏{}
,会被顺序替换成相应类型的参数println!("{} World {} {}", "Hello", true, 42);
//输出结果:
//Hello World true 42
let origin = Point { x: 10, y: 20 };
println!("origin = {}", origin)
//输出结果:
//origin = (10, 20)
更多语法可见: [rust-007]rust的println!函数的各种用法
let
将值和变量名进行绑定,声明同时必须初始化// 不可变绑定
let x = 5;// 绑定同时必须初始化
x = 10; // error: 绑定后不能改变
// 强类型的自动推导
let x = 1.000;// 编译器会推断为f32
let x : f64 = 1.000;// 类型注解
mut
表示可变绑定// 普通的mut可变
let mut x = 5;
x = 10;
// 变量遮蔽的继承可变
let x = 5; // warning:未使用的符号
let x = 10;// 可以通过let对同一符号名进行重新绑定
强类型语言
,能类型推导和进行类型安全检查类型注解
,没有注解Rust会进行类型推断,可能不符合预期内存安全
考虑,不允许使用或捕获任何未初始化的变量位置表达式 = 值表达式
,Rust没有左右值的概念,左边表达式返回内存地址,右边表达式返回值const NUMBER:i32 = 3;
let x = 5;
println!("x has the value {}", x);
let x: i32 = 10;// 注意初始化类型
if x == 10 {
println!("x is ten!");
} else {
println!("x is not ten!");
}
let x: i32 = 100;// Rust的变量使用前必须绑定值
println!("Number {}", x);
let mut x = 3;// 可变绑定
println!("Number {}", x);
x = 5; // don't change this line
println!("Number {}", x);
- const变量的绑定必须要有初始类型和值
const NUMBER:i32 = 3;
let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number);
let number = 3; // 重影方式,进行重新绑定
println!("Number plus two is : {}", number + 2);
这里实际上是对于函数 call_me,有使用但是没有声明,因此我们只需要声明一下即可。
fn call_me() {
}
函数形参必须指定类型
fn call_me(num: i32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}
}
函数形参和实参的类型和数量必须匹配
call_me(10);
函数返回值使用->
进行标识
fn sale_price(price: i32) -> i32{
if is_even(price) {
price - 10
} else {
price - 3
}
}
函数的返回值后不需要加分号
fn square(num: i32) -> i32 {
num * num
}
Rust 的 If statement 部分并不需要加括号
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
// - another function call
// - additional variables
if a > b {
a
} else {
b
}
}
这个需要根据下面的测试程序决定没一个输入应该输出什么字符串。
pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
} else {
"baz"
}
}
这个小测试实际上需要我们读懂上面的英文含义,并实现calculate_price_of_apples函数。
按照所叙述的要求,当购买数量超过 40 时,每个苹果 1 元,否则 2 元。
fn calculate_price_of_apples(cnt: i32) -> i32 {
if cnt <= 40 {
cnt << 1
} else {
cnt
}
}
可以发现下面的变量 is_evening 有使用没有定义,再考虑到这里代码的语义,可以得到需要填写的代码:
let is_evening = false; // Finish the rest of this line like the example! Or make it be false!
只需要对于 your_character 这个变量进行定义即可
let your_character = '3';
定义一个字符串数组,数组里面每一个元素均为 “qaq”,数组总长度为 666.
let a = ["qaq"; 666];
切片的变量实际上并不能获得地址的所有权的,仅仅是一个引用
let nice_slice = &a[1..4];// 前闭后开
元组的使用
let (name, age) = cat;
获得元组的某一个元素,就直接使用[元组名.index]这样的格式即可。
let numbers = (1, 2, 3);
// Replace below ??? with the tuple indexing syntax.
let second = numbers.1;// 元素的下标引用
assert_eq!(2, second,
"This is not the 2nd number in the tuple!")
使用宏 vec! 来定义一个 vector
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = vec![10, 20, 30, 40];// TODO: declare your vector here with the macro for vectors
(a, v)
}
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for i in v.iter_mut() {
// TODO: Fill this up so that each element in the Vec `v` is
// multiplied by 2.
*i <<= 1;
}
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
v
}
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|num| {
// TODO: Do the same thing as above - but instead of mutating the
// Vec, you can just return the new number!
num << 1
}).collect()
}
{
// 在声明以前,变量 s 无效
let s = "runoob";
// 这里是变量 s 的可用范围
}
// 变量范围已经结束,变量 s 无效
let s1 = String::from("hello");
let s2 = s1;
// 赋值后s1失效,指向的堆内存所有权移动给s2
let x = 5;
let y = x;
// 拷贝语义:赋值后均有效
// 堆内存的深拷贝
let s1 = String::from("hello");
let s2 = s1.clone();
栈中的基本数据类型
的赋值是拷贝语义
,在堆中的引用数据类型
的赋值是移动语义
函数返回值的临时变量
可以通过移动语义
返回到函数调用处fn main() {
let s = String::from("hello");
// s 被声明有效
takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效
let x = 5;
// x 被声明有效
makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2);
// s2 被当作参数移动, s3 获得返回值所有权
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
fn take_and_giveback(a_string:String)->String{
a_string // a_string 被当作返回值移出函数
}
// 基本引用
let s1 = String::from("hello");
let s2 = &s1;
// 所有权转移引起的租借失效
let s1 = String::from("hello");
let mut s2 = &s1;
let s3 = s1;// s1内存资源所有权转移到s3中,s2的租借失效
s2 = &s3; // 重新从 s3 租借所有权
// 可变租借
let mut s1 = String::from("run");// 被租借对象本身就是可变的
// s1 是可变的
let s2 = &mut s1;// 赋予租借者可变的权力
// s2 是可变的引用
let s3 = &mut s1;// error,不允许多重可变引用
vec1 在 11 行进行了修改,而我们在进行变量声明的时候并没有使用 mut 关键字,因此出现错误
let mut vec1 = fill_vec(vec0);
vec0 在进入函数之后,所有权就被传入进去并在函数结束后释放。因此在 fill_vec 之后就再也不能调用 vec0 了。这里解决的方式是通过 clone 的方式进行深拷贝,避免直接使用变量本身。
let vec0 = Vec::new();
let vec = vec0.clone();
let mut vec1 = fill_vec(vec);
问题的核心错误在于函数 fill_vec 的 vec 变量是不可变变量
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
问题:函数调用形参未写,并且局部变量名称相同
fn fill_vec(vec0: Vec<i32>) -> Vec<i32> {
let mut vec = vec0;
Rust1.31以后,借用的作用域的结束位置从花括号{}
变成最后一次使用的位置
fn main() {
let mut x = 100;
let y = &mut x;
*y += 100;// 最后一次使用y,使用完成后会自动释放
let z = &mut x;
*z += 1000;
assert_eq!(x, 1200);
}
注释提示了,第一个函数不应该有所有权,第二个函数应该获得所有权。
所以第一个函数使用借用,第二个函数使用移动语义进行直接传递
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}
// Should not take ownership
fn get_char(data: &String) -> char {// 形参借用也要加&
data.chars().last().unwrap()
}
// Should take ownership
fn string_uppercase(mut data: String) {
data = data.to_uppercase();
println!("{}", data);
}
// 结构的声明方式
struct ColorClassicStruct {
// TODO: Something goes here
red: i32,
green: i32,
blue: i32,
}
// 元组的声明方式
struct ColorTupleStruct(u8, u8, u8);
// 结构体的初始化及绑定
// TODO: Instantiate a classic c struct!
// let green =
let green = ColorClassicStruct {
red: 0,
green: 255,
blue: 0,
};
// 元组的初始化及绑带
let green = ColorTupleStruct(0, 255, 0);
// 实例化一个单元类
let unit_like_struct = UnitLikeStruct;
按照断言补足即可
let your_order = Order {
name: String::from("Hacker in Rust"),
year: 2019,
made_by_phone: false,
made_by_mobile: false,
made_by_email: true,
item_number: 123,
count: 1,
};
通过断言必须为真,分析代码逻辑,从而改写函数。
fn is_international(&self) -> bool {
// Something goes here...
self.sender_country != self.recipient_country
}
fn get_fees(&self, cents_per_gram: i32) -> i32 {
// Something goes here...
self.weight_in_grams * cents_per_gram
}
枚举类写法
enum Message {
// TODO: define a few types of messages as used below
Quit,
Echo,
Move,
ChangeColor,
}
在枚举类内可以定义不同类型的元素,甚至还可以定义枚举类。
enum Message {
// TODO: define the different variants used below
Move {x: i32, y: i32},
Echo(String),
ChangeColor(i32, i32, i32),
Quit
}
match函数和枚举的使用,match类似switch但是不需要break
// 枚举类
enum Message {
// TODO: implement the message variant types based on their usage below
ChangeColor((u8, u8, u8)),
Echo(String),
Move(Point),
Quit,
}
// match函数
match message{
Message::ChangeColor(t) => self.change_color(t),
Message::Echo(msg) => self.echo(msg),
Message::Move(point) => self.move_position(point),
Message::Quit => self.quit(),
};
// 括号?
state.process(Message::ChangeColor((255, 0, 255)));
切片后字符串在堆的起始地址+长度
,堆上为被引用的字符串切片
和双引号的字符串
都是&str类型let s = String::from("broadcast");
let part1 = &s[0..5];// 0,1,2,3,4
// 参数只读的打印函数
fn greet(name: &str) {// 类似与const &形参
println!("Hello, {}!", name);
}
// 切片引用不能修改原值
let mut s = String::from("runoob");
let slice = &s[0..3];
s.push_str("yes!"); // 错误
let s1 = String::from("hello");
let s2 = &s1[..];// String 转换成 &str
let s3 = s2.tostring();
片开始指针 + 大小
..
,其中x…y 表示 [x, y) 的数学含义..y // 等价于 0..y
x.. // 等价于位置 x 到数据结束
.. // 等价于位置 0 到结束
x..y // x到y前闭后开
let arr = [1, 3, 5, 7, 9];
let part = &arr[0..3];
for i in part.iter() {
println!("{}", i);
}
```### <font face="黑体" color=purple>strings.1</font>
简单的&str转换成String对象
fn current_favorite_color() -> String {
"blue".to_string()
}
参数类型的更改
fn is_a_color_word(attempt: String) -> bool {
attempt == "green" || attempt == "blue" || attempt == "red"
}
注释已经标出
fn trim_me(input: &str) -> String {
// TODO: Remove whitespace from both ends of a string!
input.trim().to_string() // trim()除了单词间的空格全部消除
}
fn compose_me(input: &str) -> String {
// TODO: Add " world!" to the string! There's multiple ways to do this!
format!("{} world!", input) // 审题
}
fn replace_me(input: &str) -> String {
// TODO: Replace "cars" in the string with "balloons"!
input.replace("cars", "balloons") // 替换单词
}
根据第一个小标题下的切片知识可以做出来
fn main() {
string_slice("blue");
string("red".to_string());
string(String::from("hi"));
string_slice("rust is fun!".to_owned());
string_slice("nice weather".into());
string_slice(format!("Interpolation {}", "Station"));
string_slice(&String::from("abc")[0..1]);
string_slice(" hello there ".trim());
string("Happy Monday!".to_string().replace("Mon", "Tues"));
string_slice("mY sHiFt KeY iS sTiCkY".to_lowercase());
}
在外面使用,因此需要定义为pub
pub fn make_sausage() {
get_secret_recipe();
println!("sausage!");
}
给模块起别名,注意末尾的分号,并且外界需要使用就加pub
pub use self::fruits::PEAR as fruit;
pub use self::veggies::CUCUMBER as veggie;
使用 use 关键字来引入标准库
use std::time::{SystemTime, UNIX_EPOCH};
哈希表的定义
let mut basket = HashMap::new();
哈希表的插入
basket.insert(String::from("apple"), 1);
basket.insert(String::from("mango"), 2);
通过 entry 获取得到针对某个字符的记录,or_insert在哈希表中检查条目中是否已经存在参数值,没有则插入
basket.entry(fruit).or_insert(1);
与上题相同,但需要逻辑判断
let score = scores.entry(team_1_name.clone()).or_insert(Team {
name: team_1_name,
goals_scored: 0,
goals_conceded: 0,
});
(*score).goals_scored += team_1_score;
(*score).goals_conceded += team_2_score;
let score = scores.entry(team_2_name.clone()).or_insert(Team {
name: team_2_name,
goals_scored: 0,
goals_conceded: 0,
});
(*score).goals_scored += team_2_score;
(*score).goals_conceded += team_1_score;
mod my_module {
use super::Command;
// TODO: Complete the function signature!
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
// TODO: Complete the output declaration!
let mut output: Vec<String> = vec![];
for (string, command) in input.iter() {
// TODO: Complete the function body. You can do it!
match command {
Command::Uppercase => {
output.push(string.to_uppercase());
}
Command::Trim => {
output.push(string.trim().to_string());
}
Command::Append(usize) => {
let mut ans = String::new();
for i in 0..*usize {
ans += &string.clone();
}
output.push(format!("{}bar", ans));
}
}
}
output
}
}
网上答案可能不行,因为从github上clone下来的项目中的路径配置与本地不同,可能导致相对路径失效,需要使用绝对路径
// TODO: What do we have to import to have `transformer` in scope?
use crate::my_module::transformer;
Some(Vaule)
,要么是None
独一性
强制打包
按要求返回值,并且在判断时需要进行Some的包含
if time_of_day > 24 {
None
} else if time_of_day >= 22 {
Some(0)
} else {
Some(5)
}
#[test]
fn raw_value() {
// TODO: Fix this test. How do you get at the value contained in the Option?
let icecreams = maybe_icecream(12);
assert_eq!(icecreams, Some(5));
}
这里需要两层 Some 的原因是因为 Vec 的 pop 函数会套一层 Option.
if let Some(word) = optional_target {
assert_eq!(word, target);
}
while let Some(Some(integer)) = optional_integers.pop() {
assert_eq!(integer, range);
range -= 1;
}
这里是需要让 match 语句不拥有所有权,而是采用借用的方式,采用 ref 关键字。
match y {
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
_ => println!("no match"),
}
enum Result<type(T), type(E)> {
Ok(T),
Err(E),
}
// 使用
if name.is_empty() {
// Empty names aren't allowed.
Err("`name` was empty; it must be nonempty.".to_string())
} else {
Ok(format!("Hi! My name is {}", name))
}
?操作符
let result :Result<T,E1> = ···;
// ?操作符的使用
let ok = result?;
ok;
// 上面语句的去糖展开式
let ok = match result{
OK(ok) => ok,// 成功则内部值T和作为表达式的返回结果
Err(err)=>return Err(From::from(err))// 失败则将操作符前的E1转换类型为Result中的E2并返回
};
fn main() {
let s1 = Some("some1");
let s2 = Some("some2");
let n: Option<&str> = None;
let o1: Result<&str, &str> = Ok("ok1");
let o2: Result<&str, &str> = Ok("ok2");
let e1: Result<&str, &str> = Err("error1");
let e2: Result<&str, &str> = Err("error2");
assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1
assert_eq!(s1.or(n), s1); // Some or None = Some
assert_eq!(n.or(s1), s1); // None or Some = Some
assert_eq!(n.or(n), n); // None1 or None2 = None2
assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2
assert_eq!(s1.and(n), n); // Some and None = None
assert_eq!(n.and(s1), n); // None and Some = None
assert_eq!(n.and(n), n); // None1 and None2 = None1
// 类型映射
let s1 = Some("abcde");
let s2 = Some(5);
let fn_character_count = |s: &str| s.chars().count();
let fn_character_count = |s: &str| s.chars().count();
// map_err(),只有Err值被改变
let o1: Result<&str, &str> = Ok("abcde");
let o2: Result<&str, isize> = Ok("abcde");
let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // convert str to isize
assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2
pub fn generate_nametag_text(name: String) -> Result<String, String> {
if name.is_empty() {
// Empty names aren't allowed.
Err("`name` was empty; it must be nonempty.".to_string())
} else {
Ok(format!("Hi! My name is {}", name))
}
}
详情见本节基础知识2
let qty = item_quantity.parse::<i32>()?;
?操作符
只能用于返回类型为Result
的函数
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(())
}
有对要有错,除了使用?操作符,进行隐式的错误类型转换返回
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
// Hmm...? Why is this only returning an Ok value?
if value < 0 {
return Err(CreationError::Negative);
} else if value == 0 {
return Err(CreationError::Zero);
}
Ok(PositiveNonzeroInteger(value as u64))
}
box作为智能指针可以用于解决编译时大小未知的问题
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(())
}
这里是需要让 match 语句不拥有所有权,而是采用借用的方式,采用 ref 关键字。
fn from_parseint(err: ParseIntError) -> ParsePosNonzeroError {
ParsePosNonzeroError::ParseInt(err)
}
//
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
占用符:使编译器自己进行推断类型,类似auto?
let mut shopping_list: Vec<_> = Vec::new();
模板类的使用
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T>{
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
// 接口
trait Hello {
fn say_hi(&self) {
println!("hi");
}
}
struct Student {}
impl Hello for Student {}// 给类添加接口
struct Teacher {}
impl Hello for Teacher {// 接口的重写Override
fn say_hi(&self) {
println!("hi, I'm teacher Lee.");
}
}
fn main() {
let s = Student {};
s.say_hi();
let t = Teacher {};
t.say_hi();
}
注释提示了,第一个函数不应该有所有权,第二个函数应该获得所有权。
所以第一个函数使用借用,第二个函数使用移动语义进行直接传递
impl AppendBar for String {
// TODO: Implement `AppendBar` for type `String`.
fn append_bar(self) -> Self {
format!("{}{}", self, "Bar")
}
}
注释提示了,第一个函数不应该有所有权,第二个函数应该获得所有权。
所以第一个函数使用借用,第二个函数使用移动语义进行直接传递
impl AppendBar for Vec<String>
{
fn append_bar(mut self) -> Self
{
self.push("Bar".to_string());
self
}
}
pub trait Licensed {
fn licensing_info(&self) -> String {
String::from("Some information")
}
}
类对象类型
fn compare_license_types(software: impl Licensed, software_two: impl Licensed) -> bool {
software.licensing_info() == software_two.licensing_info()
}
简单的assert判断
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert() {
assert!(true);
}
}
注释提示了,第一个函数不应该有所有权,第二个函数应该获得所有权。
所以第一个函数使用借用,第二个函数使用移动语义进行直接传递
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert_eq() {
assert_eq!("a", "a");
}
}
#[test]
fn is_true_when_even() {
assert!(is_even(90));
}
#[test]
fn is_false_when_odd() {
assert!(is_even(92));
}
类对象类型
pub struct ReportCard<T> {// 泛型
pub grade: T,
pub student_name: String,
pub student_age: u8,
}
impl<T: std::fmt::Display> ReportCard<T> {// std::fmt::Display用于异常显示的自定义
pub fn print(&self) -> String {
format!(
"{} ({}) - achieved a grade of {}",
&self.student_name, &self.student_age, &self.grade
)
}
}
#[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+", // 根据assert进行修改
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(
report_card.print(),
"Gary Plotter (11) - achieved a grade of A+"
);
}
// 隐式生命周期
// 编译器在编译时无法推导出返回值,只有运行时才能推导出来
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
// 显式生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
//当编译器看到x: &'a str的时候,'a会被编译器推断为x的生命周期
//当编译器看到y: &'a str的时候,编译器会将'a推断为y的生命周期
//但是此时有冲突,于是编译器会将'a推断为x和y的生命周期中最小的那个。
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
fn main() {
let x = 6;
let m;
// x和y生命周期相同,打印时已经失效
{
let y = 6;
let f = Foo { x: &x, y: &y };
m = f.x;
}
println!("{}", m);
}
当一个生命周期参数修饰多个字段的时候,编译器会将这个生命周期参数推断出这几个字段生命周期最小的那个。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
result被绑定后,在括号时被释放
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
println!("The longest string is '{}'", result);
}
}
struct Book<'a, 'b> {
author: &'a str,
title: &'b str,
}
值的不可变引用
,即&T值的可变引用
,即&mut T值的所有权
T类型的值值的不可变引用
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
// to_uppercase函数:返回字符串的大写字母
// collect函数:将一个集合的内容移动到另一个集合的主要方式
// as_str() 可以显式提取包含该字符串的字符串片段
// next函数会让迭代器指向下一个对象
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),的大写字母
}
}
// 改变vector中第一个字母为大写字母
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
let mut col = vec![];
for word in words
{
col.push(capitalize_first(word));
}
col
}
//.join(" ") 将多维数组降维成中间为空格的一维数组
pub fn capitalize_words_string(words: &[&str]) -> String {
let mut buffer = vec![];
for word in words {
buffer.push(capitalize_first(word));
//println!("{:?}", buffer);
}
buffer.join("")
}
组合器的拆分
// 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);
} else if a % b == 0 {
Ok(a / b)
} else {
return 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));
let mut buf = Vec::<i32>::new();
for n in numbers {
match divide(n, 27) {
Ok(r) => buf.push(r),
Err(e) => return Err(e),
}
}
Ok(buf)
}
// 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));
let mut buf = Vec::new();
for n in numbers {
match divide(n, 27) {
Ok(r) => buf.push(Ok(r)),
Err(e) => buf.push(Err(e)),
}
}
buf
}
pub fn factorial(num: u64) -> u64 {
(1..=num).product()// 表示1到num中所有的数字的乘积
}
// iter()获取map的迭代器
// filter()HashMap中第一个元素value相等的值
// 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, ... }
let count = map.iter().filter(|v| *v.1 == value).count();
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, ... }, ... ]
let mut my_map1 = collection[0].clone();
let mut my_map2 = collection[1].clone();
// 调用extend方法,将my_map1转换为一个迭代器,并将其键值对添加到my_map2中
my_map2.extend(my_map1.into_iter());
// 与上一个相同
let count11 = my_map2
.iter()
.filter(|v| *v.1 == Progress::Complete)
.count();
count11
}
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(||{
//创建一个子线程
for i in 1..10 {
println!("number {} in spawn thread!",i);
thread::sleep(Duration::from_millis(1));
//规定输出后的睡眠时间
}
});
//handle.join().unwrap();
//如果将join()设置在这里,则会先进行子线程,等待子线程结束后才会进入主线程执行任务
for j in 1..5 {
//这里是主线程的任务
println!("number {} in main thread!",j);
thread::sleep(Duration::from_millis(1));
}
println!("main thread end!");
handle.join().unwrap();
//通过join()让主线程结束后等待子线程结束
//如果不设置,则主线程结束后自动中断子线程
}
use std::thread;
//use std::time::Duration;
fn main() {
let v = vec![1,2,3];
let handle = thread::spawn(move ||{
println!("{:?}",v);
});
handle.join().unwrap();
println!("Hello, world!");
}
创建了10个线程,每个线程睡眠250毫秒,然后打印自己的编号和耗时
使用JoinHandle的join方法来等待每个线程结束,并获取它们的返回值
for handle in handles {
let result = handle.join().unwrap();
results.push(result);
}
unwrap函数:计算结果,如果有错误,painc并停止程序
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 = 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
status_shared.lock().unwrap().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 {}", status.lock().unwrap().jobs_completed);
}
}
#[test]
fn is_true_when_even() {
assert!(is_even(90));
}
#[test]
fn is_false_when_odd() {
assert!(is_even(92));
}
Box 类型是一个智能指针,它允许 Box 值被当作引用对待
有详细解释https://www.rustwiki.org.cn/zh-CN/book/ch15-01-box.html?highlight=box#%E4%BD%BF%E7%94%A8-boxt-%E6%8C%87%E5%90%91%E5%A0%86%E4%B8%8A%E7%9A%84%E6%95%B0%E6%8D%AE
pub enum List {
Cons(i32, Box<List>),// Box将值放在堆上,而不是栈上
Nil,
}
pub fn create_empty_list() -> List {
List::Nil
}
pub fn create_non_empty_list() -> List {
List::Cons(0, Box::new(List::Nil))
}
Box 自定义了 Drop 用来释放 box 所指向的堆空间。
Rc 记录了堆上数据的引用数量以便可以拥有多个所有者
// TODO
let saturn = Planet::Saturn(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();
// TODO
drop(earth);
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
// TODO
drop(venus);
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
// TODO
drop(mercury);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
原子引用计数 Arc,但是原子性的使用会带来性能的降低
let shared_numbers = Arc::new(numbers);// TODO
let child_numbers = Arc::clone(&shared_numbers);// TODO
// TODO
Cow::Borrowed(_) => Ok(()),
_ => panic!("expected borrowed value"),
// TODO
Cow::Owned(_) => Ok(()),
_ => panic!("expected owned value"),
// TODO
Cow::Owned(_) => Ok(()),
_ => panic!("expected borrowed value"),
Box 类型是一个智能指针,它允许 Box 值被当作引用对待
有详细解释https://www.rustwiki.org.cn/zh-CN/book/ch15-01-box.html?highlight=box#%E4%BD%BF%E7%94%A8-boxt-%E6%8C%87%E5%90%91%E5%A0%86%E4%B8%8A%E7%9A%84%E6%95%B0%E6%8D%AE
pub enum List {
Cons(i32, Box<List>),// Box将值放在堆上,而不是栈上
Nil,
}
pub fn create_empty_list() -> List {
List::Nil
}
pub fn create_non_empty_list() -> List {
List::Cons(0, Box::new(List::Nil))
}
Box 自定义了 Drop 用来释放 box 所指向的堆空间。
Rc 记录了堆上数据的引用数量以便可以拥有多个所有者
// TODO
let saturn = Planet::Saturn(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();
// TODO
drop(earth);
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
// TODO
drop(venus);
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
// TODO
drop(mercury);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
原子引用计数 Arc,但是原子性的使用会带来性能的降低
let shared_numbers = Arc::new(numbers);// TODO
let child_numbers = Arc::clone(&shared_numbers);// TODO
宏调用需要加!
my_macro!();
宏先声明后使用
宏模块需要声明
#[macro_use]
mod macros {
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
删除#[rustfmt::skip]
,Rustfmt 会遍历 mod 树,将该属性放在声明要忽略的模块的文件
//let pi = 3.14f32;
let radius = 5.00f32;
let area = f32::consts::PI * f32::powi(radius, 2);
if let Some(x) = option {
res += x;
}
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
if my_option.is_none() {
//my_option.unwrap();
}
let my_arr = &[-1, -2, -3 - 4, -5, -6];
println!("My array! Here it is: {:?}", my_arr);
let mut my_empty_vec = vec![1, 2, 3, 4, 5];
my_empty_vec.clear();
println!("This Vec is empty, see? {:?}", my_empty_vec);
let mut value_a = 45;
let mut value_b = 66;
// Let's swap these two!
std::mem::swap(&mut value_a, &mut value_b);
println!("value a: {}; value b: {}", value_a, value_b);
}
类型转换
total / values.len() as f64
impl From<&str> for Person {
fn from(s: &str) -> Person {
let (name, age) = match s.split_once(',') {
Some((name, age)) => (name.trim(), age.trim()),
_ => return Person::default(),
};
if let Ok(age) = age.parse::<usize>() {
if name.len() > 0 {
return Person {
name: String::from(name),
age,
};
}
}
Person::default()
}
}
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 splitted_item = s.split(',').collect::<Vec<&str>>();
let (name, age) = match &splitted_item[..] {
[name, age] => (
name.to_string(),
age.parse().map_err(ParsePersonError::ParseInt)?,
),
_ => return Err(ParsePersonError::BadLen),
};
if name.is_empty() {
return Err(ParsePersonError::NoName);
}
Ok(Person {
name: name.into(),
age,
})
}
}
// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
type Error = IntoColorError;
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
let (red, green, blue) = tuple;
for color in [red, green, blue] {
if !(0..=255).contains(&color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: tuple.0 as u8,
green: tuple.1 as u8,
blue: tuple.2 as u8,
})
}
}
// Array implementation
impl TryFrom<[i16; 3]> for Color {
type Error = IntoColorError;
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
for color in arr {
if !(0..=255).contains(&color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: arr[0] as u8,
green: arr[1] as u8,
blue: arr[2] as u8,
})
}
}
// Slice implementation
impl TryFrom<&[i16]> for Color {
type Error = IntoColorError;
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
if slice.len() != 3 {
return Err(IntoColorError::BadLen);
}
for color in slice {
if !(0..=255).contains(color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: slice[0] as u8,
green: slice[1] as u8,
blue: slice[2] as u8,
})
}
}
// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
type Error = IntoColorError;
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
let (red, green, blue) = tuple;
for color in [red, green, blue] {
if !(0..=255).contains(&color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: tuple.0 as u8,
green: tuple.1 as u8,
blue: tuple.2 as u8,
})
}
}
// Array implementation
impl TryFrom<[i16; 3]> for Color {
type Error = IntoColorError;
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
for color in arr {
if !(0..=255).contains(&color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: arr[0] as u8,
green: arr[1] as u8,
blue: arr[2] as u8,
})
}
}
// Slice implementation
impl TryFrom<&[i16]> for Color {
type Error = IntoColorError;
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
if slice.len() != 3 {
return Err(IntoColorError::BadLen);
}
for color in slice {
if !(0..=255).contains(color) {
return Err(IntoColorError::IntConversion);
}
}
Ok(Self {
red: slice[0] as u8,
green: slice[1] as u8,
blue: slice[2] as u8,
})
}
}
// 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) {
*arg.as_mut() *= *arg.as_mut()
}
点此跳转到首行↩︎