1. 正如 第七章 讲到的,当所需函数嵌套了多于一层模块时,通常将父模块引入作用域,而不是其自身。这便于我们利用 std::env 中的其他函数。这比增加了 use std::env::args; 后仅仅使用 args 调用函数要更明确一些,因为 args 容易被错认成一个定义于当前模块的函数。 2. 注意 std::env::args 在其任何参数包含无效 Unicode 字符时会 panic。如果你需要接受包含无效 Unicode 字符的参数,使用 std::env::args_os 代替。 3. 当函数承担了更多责任,它就更难以推导,更难以测试,并且更难以在不破坏其他部分的情况下做出修改。最好能分离出功能以便每个函数就负责一个任务。 4. 随着 main 函数的增长,就需要引入更多的变量到作用域中,而当作用域中有更多的变量时,将更难以追踪每个变量的目的。最好能将配置变量组织进一个结构,这样就能使他们的目的更明确了。 [产品化思维] 5. 读取文件失败的原因有多种:例如文件不存在,或者没有打开此文件的权限。目前,无论处于何种情况,我们只是打印出“文件读取出现错误”的信息,这并没有给予使用者具体的信息! [吐槽] 6. 注意:一些同学将这种在复杂类型更为合适的场景下使用基本类型的反模式称为 基本类型偏执(primitive obsession) 7. 在第一轮编写时拥有一个可以工作但有点低效的程序要比尝试过度优化代码更好一些。随着你对 Rust 更加熟练,将能更轻松的直奔合适的方法,不过现在调用 clone 是完全可以接受的。 8. 做出这个改变使得代码更符合习惯:可以像标准库中的 String 调用 String::new 来创建一个该类型的实例那样,将 parse_config 变为一个与 Config 关联的 new 函数。 9. 当 Result 是 Ok 时,这个方法的行为类似于 unwrap:它返回 Ok 内部封装的值。然而,当其值是 Err 时,该方法会调用一个 闭包(closure),也就是一个我们定义的作为参数传递给 unwrap_or_else 的匿名函数。 [rust对app的测试理解] 10. 讲业务逻辑抽闲到lib.rs crate中,通过单元测试来验证;对于app crate通过简明的观察来验证:一旦完成这些,main 函数将简明得足以通过观察来验证,而我们将能够为所有其他逻辑编写测试。 11. 目前只需知道 Box 意味着函数会返回实现了 Error trait 的类型,不过无需指定具体将会返回的值的类型。这提供了在不同的错误场景可能有不同类型的错误返回值的灵活性。这也就是 dyn,它是 “动态的”(“dynamic”)的缩写。 12. 因为 run 函数签名中声明成功类型返回值是 (),这意味着需要将 unit 类型值包装进 Ok 值中。Ok(()) 一开始看起来有点奇怪,不过这样使用 () 是惯用的做法,表明调用 run 函数只是为了它的副作用;函数并没有返回什么有意义的值。 13. run 并不返回像 Config::new 返回的 Config 实例那样需要 unwrap 的值。因为 run 在成功时返回 (),而我们只关心检测错误,所以并不需要 unwrap_or_else 来返回未封装的值,因为它只会是 ()。 14. 这只是众多编写软件的方法之一,不过 TDD 有助于驱动代码的设计。在编写能使测试通过的代码之前编写测试有助于在开发过程中保持高测试覆盖率 (1) 编写一个失败的测试,并运行它以确保它失败的原因是你所期望的。 (2) 编写或修改足够的代码来使新的测试通过。 (3) 重构刚刚增加或修改的代码,并确保测试仍然能通过。 (4) 从步骤 1 开始重复! 15. 注意双引号之后的反斜杠,这告诉 Rust 不要在字符串字面值内容的开头加入换行符 16. is_err 会返回 false 并将进行大小写不敏感搜索。我们并不关心环境变量所设置的 值,只关心它是否被设置了,所以检查 is_err 而不是 unwrap、expect 或任何我们已经见过的 Result 的方法。 17. 大部分终端都提供了两种输出:标准输出(standard output,stdout)对应一般信息,标准错误(standard error,stderr)则用于错误信息。这种区别允许用户选择将程序正常输出定向到一个文件中并仍将错误信息打印到屏幕上。