于2022年10月20日2022年10月20日由Sukuna发布
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)
}