ToB企服应用市场:ToB评测及商务社交产业平台

标题: Rustlings通关记录与题解 [打印本页]

作者: 傲渊山岳    时间: 2023-11-14 01:27
标题: Rustlings通关记录与题解
2023年6月19日决定对rust做一个重新的梳理,整理今年4月份做完的rustlings,根据自己的理解来写一份题解,记录在此。
周折很久,因为中途经历了推免的各种麻烦事,以及选择数据库作为未来研究方向后的一段适应过程,耽搁了很久。
2023年10月份秋冬季的开源操作系统训练营又开始了,所以我回来继续整理。继续进行我的os大业。


0. 熟悉rustlings

rustlings是一个rustOJ形式的学习平台,通过90多道题目来测试rust语法的掌握程度,第一次接触的时候会感觉非常新颖,通过rustlings进行学习也非常高效。
1. intro

2. variables

3. functions

4. if

5. quiz1

还有测试是我没想到的,quiz没有hint,那这样确实会比OJ体系更好玩一点。
写的if虽然很明显有问题,但测试用例就放在下面,所以这么写能通过测试。
  1. // Put your function here!
  2. fn calculate_price_of_apples(nums:i32) -> i32{
  3.     if nums <= 40{
  4.         nums*2
  5.     } else {
  6.         nums
  7.     }
  8. }
  9. // Don't modify this function!
  10. #[test]
  11. fn verify_test() {
  12.     let price1 = calculate_price_of_apples(35);
  13.     let price2 = calculate_price_of_apples(40);
  14.     let price3 = calculate_price_of_apples(41);
  15.     let price4 = calculate_price_of_apples(65);
  16.     assert_eq!(70, price1);
  17.     assert_eq!(80, price2);
  18.     assert_eq!(41, price3);
  19.     assert_eq!(65, price4);
  20. }
复制代码
</ul>11. strings

12. modules

这部分就开始介绍rust的项目框架了。分为package、crate、mod三层。题目主要是在说mod。
说实话这几道题还是比较简单的,项目中可能还会比较复杂。圣经中还有一个受限可见性,更细粒度的控制可见性 pub(in crate a),还没认真理解。
13. hashmaps

HashMap 中存储的是一一映射的 KV 键值对,并提供了平均复杂度为 O(1) 的查询方法
14. quiz2
  1. #[derive(Debug)]
  2. struct Package {
  3.     sender_country: String,
  4.     recipient_country: String,
  5.     weight_in_grams: i32,
  6. }
  7. impl Package {
  8.     fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package {
  9.         if weight_in_grams <= 0 {
  10.             panic!("Can not ship a weightless package.")
  11.         } else {
  12.             Package {
  13.                 sender_country,
  14.                 recipient_country,
  15.                 weight_in_grams,
  16.             }
  17.         }
  18.     }
  19.     fn is_international(&self) -> bool {
  20.         // Something goes here...
  21.         return self.sender_country != self.recipient_country;
  22.     }
  23.     fn get_fees(&self, cents_per_gram: i32) ->  i32{
  24.         // Something goes here...
  25.         self.weight_in_grams * cents_per_gram
  26.     }
  27. }
  28. #[cfg(test)]
  29. mod tests {
  30.     use super::*;
  31.     #[test]
  32.     #[should_panic]
  33.     fn fail_creating_weightless_package() {
  34.         let sender_country = String::from("Spain");
  35.         let recipient_country = String::from("Austria");
  36.         Package::new(sender_country, recipient_country, -2210);
  37.     }
  38.     #[test]
  39.     fn create_international_package() {
  40.         let sender_country = String::from("Spain");
  41.         let recipient_country = String::from("Russia");
  42.         let package = Package::new(sender_country, recipient_country, 1200);
  43.         assert!(package.is_international());
  44.     }
  45.     #[test]
  46.     fn create_local_package() {
  47.         let sender_country = String::from("Canada");
  48.         let recipient_country = sender_country.clone();
  49.         let package = Package::new(sender_country, recipient_country, 1200);
  50.         assert!(!package.is_international());
  51.     }
  52.     #[test]
  53.     fn calculate_transport_fees() {
  54.         let sender_country = String::from("Spain");
  55.         let recipient_country = String::from("Spain");
  56.         let cents_per_gram = 3;
  57.         let package = Package::new(sender_country, recipient_country, 1500);
  58.         assert_eq!(package.get_fees(cents_per_gram), 4500);
  59.         assert_eq!(package.get_fees(cents_per_gram * 2), 9000);
  60.     }
  61. }
复制代码
尽在不言中。讨论to_string into to_owned,简要总结,用to_owned就好。
15. options

圣经中Options讲解
16. error_handlings

错误处理,圣经相关内容,只不过题目更倾向于Result而不是panic
再解释一下 mod delicious_snacks {
    // TODO: Fix these use statements
    pub use self::fruits:EAR as fruit;
    pub use self::veggies::CUCUMBER as veggie;

    mod fruits {
        pub const PEAR: &'static str = "ear";
        pub const APPLE: &'static str = "Apple";
    }

    mod veggies {
        pub const CUCUMBER: &'static str = "Cucumber";
        pub const CARROT: &'static str = "Carrot";
    }
}

fn main() {
    println!(
        "favorite snacks: {} and {}",
        delicious_snacks::fruit,
        delicious_snacks::veggie
    );
} 为什么是这么个顺序,虽然它表达的意思我们大致能够理解。
这行代码的意思是,如果 s.parse() 解析成功,则将解析后的整数值赋值给 x;如果解析失败,? 操作符会立即返回并将 ParseIntError 转换为 ParsePosNonzeroError:arseInt 错误,并将其作为 parse_pos_nonzero 函数的返回结果。
在 Rust 中,? 操作符用于简化错误处理的过程。它只能在返回 Result 或 Option 的函数中使用。当使用 ? 操作符时,编译器会自动为你处理错误的传播。
具体到代码中,s.parse() 返回的是一个 Result,该结果表示解析字符串为整数的过程。如果解析成功,返回 Ok 包含解析后的整数值;如果解析失败,则返回 Err 包含一个 ParseIntError 错误。
17. generics

大名鼎鼎的泛型,我在C++那边都还没有好好学习,就过来rust这边再看一遍了。泛型对编程语言是极其重要的,它意味着可以用同一功能的函数处理不同类型的数据。
剩下的东西就是const泛型和泛型的性能,本来不打算看了,但是看到const泛型适用于内存较小,所以我继续看了一下。"在泛型参数之前,Rust 完全不适合复杂矩阵的运算,自从有了 const 泛型,一切即将改变。"
18. traits

特征Trait--圣经相关,特征跟接口的作用类似。跟大二学的java的抽象接口很像,以及C++的抽象类,就是特征trait中只定义共同行为,但是不描述具体行为的实现,具体行为的实现交给符合该特征的具体类型中来实现。
特征看起来好难。
题目虽然做完了,但是对特征的高级一点的用法还是不太理解它的逻辑。

知识补充:vector.push()返回值与所有权

在 Rust 中,Vec 的 push 方法用于将一个元素添加到向量(Vec)的末尾。让我们来详细讲解一下它的返回值类型和所有权。
push 方法的签名如下:
  1. // Hmm...? Why is this only returning an Ok value?
  2. if value > 0{
  3.    Ok(PositiveNonzeroInteger(value as u64))
  4. } else if value == 0 {
  5.       Err(CreationError::Zero)
  6.   } else {
  7.       Err(CreationError::Negative)
  8. }
复制代码
返回值类型是 (),也被称为“单元类型”或“空元组”。这意味着 push 方法不返回具体的值,仅用于修改 Vec 本身。
关于所有权,当调用 push 方法时,向量会获取传入元素(value)的所有权。也就是说,在调用 push 方法之后,传入的元素将成为向量的一部分,向量会负责管理它的生命周期和内存。这意味着传入的元素不再属于原来的所有者,而是属于向量。
此外,需要注意的是,为了能够修改向量,我们需要将 &mut self 作为方法的第一个参数。这表示我们需要拥有向量的可变引用,以便能够对其进行修改。
下面是一个使用 push 方法的示例:
  1. use std::error::Error;
  2. use std::fs::File;
  3. fn main() -> Result<(), Box<dyn Error>> {
  4.     let f = File::open("hello.txt")?;
  5.     Ok(())
  6. }
复制代码
在上述代码中,我们创建了一个空的 Vec,然后调用 push 方法将整数 10 添加到向量的末尾。在调用 push 方法之后,vec 将会拥有整数 10 的所有权。
19. quiz3

这里是多重特征的一个实现实例。注意这里由于impl块中使用了format!这种宏,所以还要声明使用了Display特征。通过在 impl 声明中指定 T: std::fmt:isplay,表明泛型类型 T 必须实现 std::fmt:isplay trait。
  1. let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
复制代码
20. lifetimes

生命周期!用rust写编译器的时候最烦这方面的问题,这次来好好看看。"生命周期很可能是 Rust 中最难的部分."圣经书上相关部分
简单来说,程序员如果对生命周期判断不好,就会引发程序的隐藏问题,并且很难被发现。而rust在编译器层次实现了生命周期的检查。与之适配的,为了通过生命周期检查,写rust的时候有时候需要手动标注生命周期(其他语言和此前的rust都是编译器自动推导生命周期)。
生命周期主要是解决悬垂引用问题。可以对生命周期进行下总结:生命周期语法用来将函数的多个引用参数和返回值的作用域关联到一起,一旦关联到一起后,Rust 就拥有充分的信息来确保我们的操作是内存安全的。
剩下的内容就是rust编译器更贴心一点的生命周期消除规则、方法中的生命周期(主要在说self的第三规则和生命周期约束)、静态生命周期,差不多看明白了,感觉比特征更好理解一些,特征还要回去看看。
到这里我不太熟悉的是 modules 和 generics 这两部分,其他的感觉还好。生命周期仔细看完圣经觉得还可以接受。
21. tests

22. Iterators

迭代器
23. smart_pointers

智能指针

这部分说实话,真的看的云里雾里,只是把题目做了,记住了一些语法和函数,机制还没有仔细理解。
24. threads

多线程 in rust圣经,说起来,C++的多线程只在os理论课结课的一个大作业中使用过,当时实现的是大文件数据的多线程归并排序,但是掌握的并不深刻,rust看到这,可以说在我技能树里已经超过C++了。
25. macros

macro宏编程,我在rust-cc编译器的错误消息部分就是用宏来处理错误,可以说是rust高级进阶部分比较熟悉的一部分了。
rustfmt::skip 是一种 #[attribute],用于告诉 Rustfmt 工具不要格式化注解所标记的代码。有时候,我们可能需要避免 Rustfmt 对某些代码进行格式化处理,例如对于已经手动格式化好的代码块或宏定义等。
26. clippy

clippy是一个rust代码分析工具,可以这么认为。可以检查代码中的潜在问题,如竞争条件、未使用的变量、不必要的类型强制转换等等。借助 Clippy 的 lint,您可以更轻松地发现一些常见的编码错误,并编写出更加优雅、可读性高的代码。可以通过运行 cargo clippy 命令来对当前项目进行代码分析。Clippy 将会输出所有侦测到的问题列表,可以根据这些提示优化代码。
27. conversions

类型转换
convert in 中文手册
From_str in 中文手册
我以为这部分是介绍两两类型如何互转,结果是一系列针对类型转换的处理,这种处理是基于Rust的Trait特性的。
X. 写在最后

2023年10月22日夜于图书馆七楼,趁着10月份旅游、回家修养后的旺盛精力,终于一鼓作气将rustlings的95道题完整梳理了一遍,把rust圣经更深入地看了一遍。
期间产生了很大的怀疑,因为rust也算博大高深,自己之前学习的效果在我现在看来并不明显,很多概念都还没有看明白,开始怀疑自己用rust写的c编译器很糟糕。
但是总算看完了,也是我梳理总结知识树完成的第一个小步。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4