Rustilings 练习笔记
1-1
// variables1.rs // Make me compile! // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { x = 5; println!("x has the value {}", x); }
这题简单,声明的方式出错了
// variables1.rs // Make me compile! // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. fn main() { let x = 5; println!("x has the value {}", x); }
1-2
// variables2.rs // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let x; if x == 10 { println!("x is ten!"); } else { println!("x is not ten!"); } }
Rust不能判断x的类型,指定一下就好了
// variables2.rs // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. fn main() { let x : i32 = 10; if x == 10 { println!("x is ten!"); } else { println!("x is not ten!"); } }
1-3
// variables3.rs // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let x: i32; println!("Number {}", x); }
x没有初始化,Rust编译器不允许访问没有初始化的元素
// variables3.rs // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. fn main() { let x: i32 = 10; println!("Number {}", x); }
1-4
// variables4.rs // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let x = 3; println!("Number {}", x); x = 5; // don't change this line println!("Number {}", x); }
Rust默认变量不可变,x=5修改了变量,所以说会拒绝,所以说要让变量可变
// variables4.rs // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. fn main() { let mut x = 3; println!("Number {}", x); x = 5; // don't change this line println!("Number {}", x); }
1-5
// variables5.rs // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); }
Rust有类型检查,执行运算或者赋值时候要遵循类型的规律,但是Rust可以重新定义同名变量,变量的类型可以发生改变
// variables5.rs // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); let number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); }
1-6
// variables6.rs // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE const NUMBER = 3; fn main() { println!("Number {}", NUMBER); }
const也是一样,当系统不能判断其类型的时候,需要显示说明
// variables6.rs // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. const NUMBER : i32 = 3; fn main() { println!("Number {}", NUMBER); }
2-1
// functions1.rs // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { call_me(); }
这一题我们需要自己声明一个无参数无返回值的函数.fn 名字 (){}
// functions1.rs // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. fn call_me(){ println!("Hello World"); } fn main() { call_me(); }
2-2
// functions2.rs // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { call_me(3); } fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } }
有参数的函数.参数在括号里面,用逗号隔开,名字:类型
// functions2.rs // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. fn main() { call_me(3); } fn call_me(num:i32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } }
2-3
// functions3.rs // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { call_me(); } fn call_me(num: u32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } }
很难看不出来是函数调用的时候漏传实参了,补上实参即可
2-4
// functions4.rs // Execute `rustlings hint functions4` or use the `hint` watch subcommand for a hint. // This store is having a sale where if the price is an even number, you get // 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. // (Don't worry about the function bodies themselves, we're only interested // in the signatures for now. If anything, this is a good way to peek ahead // to future exercises!) // I AM NOT DONE fn main() { let original_price = 51; println!("Your sale price is {}", sale_price(original_price)); } fn sale_price(price: i32) -> { if is_even(price) { price - 10 } else { price - 3 } } fn is_even(num: i32) -> bool { num % 2 == 0 }
函数返回返回值的方法 就是 -> 返回值类型 ,返回值可以是最后一条语句的值(这个语句不要加分号),也可以定义return
给sale_price的->后面添加上返回值类型i32即可
2-5
// functions5.rs // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let answer = square(3); println!("The square of 3 is {}", answer); } fn square(num: i32) -> i32 { num * num; }
定义返回值不是这么定义的,一般是语句或者是return,所以说要么改成return num * num;要么去掉分号.一个块中最后一条语句去掉分号、这个可以称为块尾,就是返回值
3-1
写一个bigger函数,判断返回a和b中大的那个:
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 } }
3-2
修改这个函数,使得可以编译并且能够通过测试
pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else { 1 } }
第一个是,返回的是&str,不能返回1这个数字,然后修改逻辑能够通过测试就好
pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else if fizzish == "fuzz" { "bar" } else{ "baz" } }
4-1
// primitive_types1.rs // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) // I AM NOT DONE fn main() { // Booleans (`bool`) let is_morning = true; if is_morning { println!("Good morning!"); } let // Finish the rest of this line like the example! Or make it be false! if is_evening { println!("Good evening!"); } }
只用完成一行的代码就好了.不知道这题的意义是什么23333 就是写成let is_evening = false;
就好了
4-2
// primitive_types2.rs // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) // I AM NOT DONE fn main() { // Characters (`char`) // Note the _single_ quotes, these are different from the double quotes // you've been seeing around. let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("Alphabetical!"); } else if my_first_initial.is_numeric() { println!("Numerical!"); } else { println!("Neither alphabetic nor numeric!"); } let your_character = '1';// Finish this line like the example! What's your favorite character? // Try a letter, try a number, try a special character, try a character // from a different language than your own, try an emoji! if your_character.is_alphabetic() { println!("Alphabetical!"); } else if your_character.is_numeric() { println!("Numerical!"); } else { println!("Neither alphabetic nor numeric!"); } }
代码就是给定一个字符,进行判断而已.这题似乎也没难度,填写一个:let your_character = '1';
就好
4-3
这题需要我们学会怎么声明一个数组,这个和C语言几乎一样.就是let a = [1,2,3,4];
4-4
这题需要我们创建一个数组slice,就是取第2-4个元素
// primitive_types4.rs // Get a slice out of Array a where the ??? is so that the test passes. // Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand for a hint. #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; let nice_slice = ???; assert_eq!([2, 3, 4], nice_slice) }
答案是&a[1..4],这个slice:&a[a,b]会取a+1到b的元素.
4-5
这题需要我们写一个元组模式匹配的代码,就是(a,b)=元组
// primitive_types5.rs // Destructure the `cat` tuple so that the println will work. // Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let cat = ("Furry McFurson", 3.5); let /* your pattern here */ = cat; println!("{} is {} years old.", name, age); }
把注释改成let (name,age) = cat;就好,这样可以像SML一样完成模式匹配
4-6
这题需要我们取出元组的第2个元素.
// primitive_types6.rs // Use a tuple index to access the second element of `numbers`. // You can put the expression for the second element where ??? is so that the test passes. // Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE #[test] fn indexing_tuple() { let numbers = (1, 2, 3); // Replace below ??? with the tuple indexing syntax. let second = ???; assert_eq!(2, second, "This is not the 2nd number in the tuple!") }
答案就是let second = numbers.1;
5-1
// move_semantics1.rs // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. fn main() { let vec0 = Vec::new(); let vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
讲vec1改成mut(可变)即可,对Vec做增删改需要将Vec改为可变
5-2
// move_semantics2.rs // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); // Do not change the following line! println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
vec0的所有权进入到函数fill_vec中了,fill_vec结束后,vec0的所有权被释放,main丧失了对vec0的所有权,就不能访问vec0了.
所以说我们要把传vec0改成传vec0的引用,传引用是不会交出所有权的.在函数中对此引用产生一个拷贝,将此拷贝传回来,这个拷贝是实际的Vec类型而不是引用
// move_semantics2.rs // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(&vec0); // Do not change the following line! println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: &Vec<i32>) -> Vec<i32> { let mut vec = (*vec).to_vec(); vec.push(22); vec.push(44); vec.push(66); (*vec).to_vec() }
5-3
fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { vec.push(22); vec.push(44); vec.push(66); vec }
主要的问题就是fill_vec函数中的vec是不可变的,要push内容是不可以的.
一个解决方法就是创建一个拷贝,像5-1一样.
第二个方法就是将vec0设置成mut的,fill_vec的vec也设置成mut
fn main() { let mut vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> { vec.push(22); vec.push(44); vec.push(66); vec }
5-4
// move_semantics4.rs // Refactor this code so that instead of passing `vec0` into the `fill_vec` function, // the Vector gets created in the function itself and passed back to the main // function. // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand for a hint. fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } // `fill_vec()` no longer takes `vec: Vec<i32>` as argument fn fill_vec() -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
不用vec0就把vec0相关的内容删掉,改成在函数体内new一个就好
fn main() { let mut vec1 = fill_vec(); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } // `fill_vec()` no longer takes `vec: Vec<i32>` as argument fn fill_vec() -> Vec<i32> { let mut vec = Vec::new(); vec.push(22); vec.push(44); vec.push(66); vec }
5-5
// move_semantics5.rs // Make me compile only by reordering the lines in `main()`, but without // adding, changing or removing any of them. // Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let mut x = 100; let y = &mut x; let z = &mut x; *y += 100; *z += 1000; assert_eq!(x, 1200); }
不能对一个变量同时创建两个可变引用,改成这样就好:
fn main() { let mut x = 100; let z = &mut x; *z += 100; *z += 1000; assert_eq!(x, 1200); }
5-6
// move_semantics6.rs // Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand for a hint. // You can't change anything except adding or removing references. 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); }
代码的注释都写了,不要放弃所有权就是加引用,放弃所有权就是不加引用
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); }
6-1
这一题我们要了解最基础的结构体、元组型结构体以及单元结构体的定义和初始化.
定义的方法两者有所不同,一个是用{}扩起来,不同的元素之间用逗号隔开,还有就是元组型,用()扩起来,不同的元素用逗号隔开.访问的方式一个是结构体.名字和结构体.顺序
// structs1.rs // Address all the TODOs to make the tests pass! // Execute `rustlings hint structs1` or use the `hint` watch subcommand for a hint. struct ColorClassicStruct { red : i32, blue : i32, green : i32, } struct ColorTupleStruct(i32,i32,i32); #[derive(Debug)] struct UnitLikeStruct; #[cfg(test)] mod tests { use super::*; #[test] fn classic_c_structs() { // TODO: Instantiate a classic c struct! let green = ColorClassicStruct{ red:0, green:255, blue:0, }; assert_eq!(green.red, 0); assert_eq!(green.green, 255); assert_eq!(green.blue, 0); } #[test] fn tuple_structs() { // TODO: Instantiate a tuple struct! let green = ColorTupleStruct(0, 255, 0); assert_eq!(green.0, 0); assert_eq!(green.1, 255); assert_eq!(green.2, 0); } #[test] fn unit_structs() { // TODO: Instantiate a unit-like struct! let unit_like_struct = UnitLikeStruct{}; let message = format!("{:?}s are fun!", unit_like_struct); assert_eq!(message, "UnitLikeStructs are fun!"); } }
6-2
这一题我们需要用一个已经定义好的结构体来声明一个结构体,有一种朴素的方法是,使用A : others.A来定义,还有就是先定义有不同的,再使用..other定义相同的属性.
// structs2.rs // Address all the TODOs to make the tests pass! // Execute `rustlings hint structs2` or use the `hint` watch subcommand for a hint. #[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 = 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); } }
6-3
这一题我们需要完成Package的impl块
fn is_international(&self) -> ??? { // Something goes here... } fn get_fees(&self, cents_per_gram: i32) -> ??? { // Something goes here... }
函数都很好写,然后可以这样调用:Package::get_fees来调用impl的方法
impl Package { fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package { if weight_in_grams <= 0 { panic!("Can not ship a weightless package.") } else { Package { sender_country, recipient_country, weight_in_grams, } } } fn is_international(&self) -> bool { if self.sender_country == self.recipient_country{ false } else{ true } } fn get_fees(&self, cents_per_gram: i32) -> i32 { self.weight_in_grams * cents_per_gram } }
比如说Package::new()就可以生成一个新的Package块
对于impl块的调用,我们给出了两种形式,一种是类::方法,一种是实例.方法
fn create_international_package() { let sender_country = String::from("Spain"); let recipient_country = String::from("Russia"); let package = Package::new(sender_country, recipient_country, 1200); assert!(package.is_international()); }
7-1
这一个我们仅需要声明一个枚举类型即可,声明的方法就是enum 名字{},{}内内容用枚举括起来.
// enums1.rs // No hints this time! ;) #[derive(Debug)] enum Message { // TODO: define a few types of messages as used below Quit, Echo, Move, ChangeColor, } fn main() { println!("{:?}", Message::Quit); println!("{:?}", Message::Echo); println!("{:?}", Message::Move); println!("{:?}", Message::ChangeColor); }
7-2
这次的声明需要我们像书上说的那样,让枚举变量的每一个枚举值和一个结构体匹配.可能不仅仅是一个结构体,有可能是一个元组或者仅仅是一个值.
// enums2.rs // Execute `rustlings hint enums2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE #[derive(Debug)] enum Message { Move{x : i32, y : i32}, Echo(String), ChangeColor(i32,i32,i32), Quit, } impl Message { fn call(&self) { println!("{:?}", &self); } } fn main() { let messages = [ Message::Move { x: 10, y: 30 }, Message::Echo(String::from("hello world")), Message::ChangeColor(200, 255, 255), Message::Quit, ]; for message in &messages { message.call(); } }
这种特性很好记忆,只需要把枚举值当成一种结构体的类型就好了.
7-3 这一题可能会有点难,我们需要根据我们已经写好的枚举类型特出特定的匹配执行特定的操作,匹配的格式是match 表达式{},{}里面是 表达式 => 做什么、返回什么
// enums3.rs // Address all the TODOs to make the tests pass! // Execute `rustlings hint enums3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE enum Message { Move(Point), Echo(String), ChangeColor((u8,u8,u8)), Quit, } struct Point { x: u8, y: u8, } struct State { color: (u8, u8, u8), position: Point, quit: bool, } impl State { fn change_color(&mut self, color: (u8, u8, u8)) { self.color = color; } fn quit(&mut self) { self.quit = true; } fn echo(&self, s: String) { println!("{}", s); } fn move_position(&mut self, p: Point) { self.position = p; } fn process(&mut self, message: Message) { match message{ Message::Move(point) => { State::move_position(self,point); }, Message::Quit => { State::quit(self); }, Message::ChangeColor(color) => { State::change_color(self,color); } Message::Echo(mes) => { State::echo(self,mes); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_match_message_call() { let mut state = State { quit: false, position: Point { x: 0, y: 0 }, color: (0, 0, 0), }; state.process(Message::ChangeColor((255, 0, 255))); state.process(Message::Echo(String::from("hello world"))); state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit); assert_eq!(state.color, (255, 0, 255)); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); assert_eq!(state.quit, true); } }
注意有一点,再引用枚举类型的时候别忘记加::来规定,Message::Move和Move是不一样的,别忘了加Message::Move.
8-1
这一题需要我们把这个模块里面的某个函数隐藏起来
mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { String::from("Ginger") } fn make_sausage() { get_secret_recipe(); println!("sausage!"); } } fn main() { sausage_factory::make_sausage(); }
其实这个很简单,因为在一个模块中,模块里面的任何一个元素都是隐藏的,或者说私有的,在前面加上一个pub就是.
8-2
这一题需要我们使用use来将一些模块添加到我们的“相对变量环境”中,如同Linux的PATH一样.
还需要我们了解as的用法.
mod delicious_snacks { // TODO: Fix these use statements pub use self::fruits::PEAR as fruit; pub use self::veggies::CUCUMBER as veggie; mod fruits { pub const PEAR: &'static str = "Pear"; pub const APPLE: &'static str = "Apple"; } mod veggies { pub const CUCUMBER: &'static str = "Cucumber"; pub const CARROT: &'static str = "Carrot"; } }
当然,还有一点就是,如果use前面加了个pub,就是说所有的模块都能使用这个use的结果,如果没加pub,代表仅限delicious_snacks这个模块能使用这个use的结果.
8-3
这一题需要我们use std里面的库,这里我使用glob运算混过去了
// TODO: Complete this use statement use std::time::*; fn main() { match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), Err(_) => panic!("SystemTime before UNIX EPOCH!"), } }
9-1
用vec宏声明一个新的Vector.这道题负责了解Vec!和直接声明的区别.
fn array_and_vec() -> ([i32; 4], Vec<i32>) { let a = [10, 20, 30, 40]; // a plain array let v = vec!(10,20,30,40); (a, v) }
用直接声明是一个[i32,n]的类型,用vec!()宏可以声明一个Vec<T>类型的变量.
9-2
这一题需要我们把Vec里面的所有元素*2.
我们有两种版本,一个是使用迭代器,依次迭代解引用更改即可.还可以使用map方法对里面的元素统一使用一个函数进行更改
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> { for i in v.iter_mut() { *i = *i * 2; } // 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 * 2 }).collect() }
10-1
改错
// strings1.rs // Make me compile without changing the function signature! // Execute `rustlings hint strings1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let answer = current_favorite_color(); println!("My current favorite color is {}", answer); } fn current_favorite_color() -> String { "blue" }
“blue”不是String类型的,它是一个常量字符串,两种改法:改成let赋值模式、或者改成返回&‘static 类型就好.
10-2
改错,也是一样的,传进来的是一个String类型的元素,但是函数的签名却是&str
// strings2.rs // Make me compile without changing the function signature! // Execute `rustlings hint strings2` or use the `hint` watch subcommand for a hint. fn main() { let word = String::from("green"); // Try not changing this line :) if is_a_color_word(word) { println!("That is a color word I know!"); } else { println!("That is not a color word I know."); } } fn is_a_color_word(attempt: &str) -> bool { attempt == "green" || attempt == "blue" || attempt == "red" }
所以说把函数签名的&str改成string就好.
10-3
写3个很简单的小函数.
一个是在后面加一个world,就是使用push_str函数在后面加上即可.
一个是去掉前面和后面的空格.就把每个单词分开来,然后组装在一起.
一个是把所有的cars换成balloons,也是把每个单词分开来,加一个判断而已.
fn trim_me(input: &str) -> String { // TODO: Remove whitespace from both ends of a string! let s = input.to_string(); let mut ne = String::new(); for word in s.split_whitespace(){ ne.push_str(word); ne.push_str(" "); } ne.pop(); ne } fn compose_me(input: &str) -> String { // TODO: Add " world!" to the string! There's multiple ways to do this! let mut s = input.to_string(); s.push_str(" world!"); s } fn replace_me(input: &str) -> String { // TODO: Replace "cars" in the string with "balloons"! let s = input.to_string(); let mut ne = String::new(); for word in s.split_whitespace(){ if word == "cars"{ ne.push_str("balloons"); } else{ ne.push_str(word); } ne.push_str(" "); } ne.pop(); ne }
10-4
判断是String还是&str的?
fn string_slice(arg: &str) { println!("{}", arg); } fn string(arg: String) { println!("{}", arg); } fn main() { ???("blue"); ???("red".to_string()); ???(String::from("hi")); ???("rust is fun!".to_owned()); ???("nice weather".into()); ???(format!("Interpolation {}", "Station")); ???(&String::from("abc")[0..1]); ???(" hello there ".trim()); ???("Happy Monday!".to_string().replace("Mon", "Tues")); ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); }
答案就是这个:
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()); }
11-1
这一题需要我们声明一个空的HashMap,然后插入若干个key-value对.插入就调用insert就好了.
use std::collections::HashMap; fn fruit_basket() -> HashMap<String, u32> { let mut basket = HashMap::new();// TODO: declare your hash map here. // Two bananas are already given for you :) // TODO: Put more fruits in your basket here. basket.insert(String::from("banana"), 2); basket.insert(String::from("Apple"), 2); basket.insert(String::from("Orange"), 2); basket }
11-2
这一题需要我们完成哈希表的判断插入,当这个表没有这个元素的时候才进行插入操作,在书本上说过可以用enrty和or_insert进行插入
fn fruit_basket(basket: &mut HashMap<Fruit, u32>) { let fruit_kinds = vec![ Fruit::Apple, Fruit::Banana, Fruit::Mango, Fruit::Lychee, Fruit::Pineapple, ]; for fruit in fruit_kinds { // TODO: Put new fruits if not already present. Note that you // are not allowed to put any type of fruit that's already // present! let k = basket.entry(fruit).or_insert(1); } }
11-3
这一题需要我们统计一个球队输球和赢球的数量,需要用到or_insert,用到or_insert的返回值,这个返回值是一个引用,可以改变value的值.
use std::collections::HashMap; // A structure to store team name and its goal details. struct Team { name: String, 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 number of goals conceded from team_2, and similarly // goals scored by team_2 will be the number of goals conceded by let g = scores.entry(team_1_name.to_string()).or_insert(Team{name:team_1_name,goals_scored:0,goals_conceded:0}); (*g).goals_scored = (*g).goals_scored + team_1_score; (*g).goals_conceded = (*g).goals_conceded + team_2_score; let f = scores.entry(team_2_name.to_string()).or_insert(Team{name:team_2_name,goals_scored:0,goals_conceded:0}); (*f).goals_scored = (*f).goals_scored + team_2_score; (*f).goals_conceded = (*f).goals_conceded + team_1_score; } scores }
12-1
这一题单纯用来了解Result的特征,Result<T,E>是一个枚举类型,其中Ok的类型是T,Err的类型是E,如果一个函数会返回Result在不同情况下会返回Ok或者Err两种元素.
这一题的T和E都是字符串,负责传回一个字符串.
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.".into()) } else { Ok(format!("Hi! My name is {}", name)) } }
12-2
这一题我们需要了解最基本的错误的处理方式,第一种处理方式就是match一个Result值,Result值有两种枚举的可能,一个是Ok(T),一个是Err(E),分步处理即可.如果是Err就要直接返回.
use std::num::ParseIntError; pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { let processing_fee = 1; let cost_per_item = 5; let qty = match item_quantity.parse::<i32>(){ Ok(T) => T, Err(E) => { return Err(E); }, }; Ok(qty * cost_per_item + processing_fee) }
12-3
这一题主要是了解Result的?用法.
如果加上了?这个符号在一个可以返回Result<T,E>的函数调用后面,这个符号可以完成这个任务:
如果是Ok,这个表达式的值就是Ok的值,如果是Err,就会自动向上面传递问题.
use std::num::ParseIntError; fn main() { 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); } } 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>()?; Ok(qty * cost_per_item + processing_fee) }
这个代码里面就是有一个?,item_quantity.parse::<i32>()?
没问题就返回这个i32,如果有问题就向上面抛出异常.
上层需要处理这个异常.一种改法是让main也返回Result类型,还有一种就是把?去掉加上match函数进行处理.
let cost = match total_cost(pretend_user_input){ Ok(a) => a, Err(E) => { panic!("G"); } };
12-4
这一题和上一题一样,教我们怎么返回Ok或者抛出异常.
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) } } }
12-5
main函数能返回Result类型:
将???改为error::Error,指动态匹配错误的类型.完成了fmt::Display就是error::Error类型了!
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 ???>> { 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 {}
12-6
这一个就是在Err(E)中加了点手脚,就是Err(E)中E的类型也是一个Err类型.
这里是创建了一个新的Err类型,Err类型中有两种不同的枚举值.对于不同的枚举值代表两种不同的错误.
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) } fn from_parseint(err: ParseIntError) -> ParsePosNonzeroError{ ParsePosNonzeroError::ParseInt(err) } // TODO: add another error conversion function here. // fn from_parseint... } 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: i64 = match s.parse(){ Ok(T) => T, Err(E) => { return Err(ParsePosNonzeroError::from_parseint(E)); }, }; PositiveNonzeroInteger::new(x) .map_err(ParsePosNonzeroError::from_creation) }
13-1
返回一个Option枚举值,如果时间小于24,就返回Option<i32>,如果大于24,就是不对的值,要返回None.这一题教我们如何返回Option类型的值
fn maybe_icecream(time_of_day: u16) -> Option<u16> { // We use the 24-hour system here, so 10PM is a value of 22 // The Option output should gracefully handle cases where time_of_day > 24. if time_of_day > 24{ None } else if time_of_day > 12{ Some(0) } else{ Some(5) } } #[cfg(test)] mod tests { use super::*; #[test] fn check_icecream() { assert_eq!(maybe_icecream(9), Some(5)); assert_eq!(maybe_icecream(10), Some(5)); assert_eq!(maybe_icecream(23), Some(0)); assert_eq!(maybe_icecream(22), Some(0)); assert_eq!(maybe_icecream(25), None); } #[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)); } }
13-2
这一题需要我们了解if-let和while-let的用法,if-let和while-let的核心用法就是匹配.
if let Some(i) = 一个Option<T>值,当匹配的时候,产生一个新的变量i并且执行下面的操作.while-let也是如此
核心的思想就是Some(i) = 一个Option值,然后这样可以巧妙地提取Option的内容并且避免冗杂的match表达式
#[cfg(test)] mod tests { use super::*; #[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 mut range = 10; let mut optional_integers: Vec<Option<i8>> = Vec::new(); for i in 0..(range + 1) { optional_integers.push(Some(i)); } // 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, range); range -= 1; } } }
13-3
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), _ => println!("no match"), } y; // Fix without deleting this line. }
再取Option里面的结构体的时候,我们需要知道,结构体在option里面,所有权是归Option值的,后面又访问了一次Option值,所以说y还没有放弃所有权,只能通过引用去访问.(y没有放弃所有权,里面的结构体也是属于y的,y= Some(p)来取数据是不对的)
14-1
Vec是一个带有泛型(参数是类型)类型的结构,我们声明的时候需要添加泛型参数.
fn main() { let mut shopping_list: Vec<?> = Vec::new(); shopping_list.push("milk"); }
?替换成String就好,因为需要声明为Vec<String>,因为Vec<String>是类型但是Vec<>不是类型.
14-2
将这个代码改成泛型,记住函数、结构体、impl块的泛型类型
struct Wrapper<T> { value: T, } impl<T> Wrapper<T> { pub fn new(value: T) -> Self { Wrapper { value } } }
15-1
完成代码,完成AppendBar这个trait,注意trait块里面的接口的返回值有可能是Self类型的,代表和本身是一个类型的.
trait AppendBar { fn append_bar(self) -> Self; } impl AppendBar for String { //Add your code here fn append_bar(mut self) -> String{ self.push_str("Bar"); self } } 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") ); } }
15-2
trait AppendBar { fn append_bar(self) -> 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")); } }
从题目中我们可以知道是要给Vec<String>实现AppendBar这个trait,语法和上面一样
impl AppendBar for Vec<String>{ fn append_bar(mut self) -> Vec<String>{ self.push("Bar".to_string()); self } }
15-3
这一题需要我们修改trait里面方法的默认实现模式
改成这样:
pub trait Licensed { 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); } }
15-4
这里需要我们用trait和泛型配合,使得函数的传参只能传已经实现某种trait的结构体
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: ??, software_two: ??) -> 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)); } }
把函数签名改成fn compare_license_types<T : Licensed,E : Licensed>(software: T, software_two: E) -> bool
就好
15-5
这里需要我们再15-4的基础上再进一步,函数的传参只能传已经实现多种trait的结构体.
// 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. // I AM NOT DONE 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: ??) -> bool { item.some_function() && item.other_function() } fn main() { some_func(SomeStruct {}); some_func(OtherStruct {}); }
改成这样就好
16-1
assert!(表达式),如果表达式为true还好,表达式为false就panic
#[cfg(test)] mod tests { #[test] fn you_can_assert() { assert!(1 < 2); } }
16-2
asserteq!(A,B),A=B是没事,反之panic
#[cfg(test)] mod tests { #[test] fn you_can_assert_eq() { assert_eq!(1,1); } }
16-3
自己编写test,挺简单的.
17-1
fn longest(x: &str, y: &str) -> &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); }
书本里的例子,作为函数要去判断传参和返回的引用符不符合生命周期的标准,也就是说传参的生命周期要和返回的生命周期匹配并且满足可以省略的要求,如果不满足可以省略的要求就需要标注生命周期标注
不满足隐藏的条件显示声明生命周期,统一成一样的.即fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
17-2
基于17-1,因为’a作为生命周期泛型,匹配传参中生命周期最短的生命周期,然后把这个生命周期返回出去,但是返回出去的生命周期不能支撑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); }
改成这样:
fn main() { let string1 = String::from("long string is long"); { let string2 = String::from("xyz"); let result = longest(string1.as_str(), string2.as_str()); println!("The longest string is '{}'", result); } }
17-3
结构体里面有引用,则需要保证结构体里面引用成员的生命周期要比结构体长.
所以说要配一个泛型参数,指定结构体的生命周期是所有引用成员里面最小的那个即可.这就是泛型参数的作用
struct Book { author: &str, title: &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); }
生命周期是需要在类型后面加上’xxx的,注意一下’xxx的语法
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); }
0 条评论