diff --git "a/src/safe-guides/coding_practice/error-handle/G.ERR.01 \345\234\250\345\244\204\347\220\206 `OptionT` \345\222\214 `Result T, E` \347\261\273\345\236\213\346\227\266\357\274\214\344\270\215\350\246\201\351\232\217\344\276\277\344\275\277\347\224\250 `unwrap`.md" "b/src/safe-guides/coding_practice/error-handle/G.ERR.01 \345\234\250\345\244\204\347\220\206 `OptionT` \345\222\214 `Result T, E` \347\261\273\345\236\213\346\227\266\357\274\214\344\270\215\350\246\201\351\232\217\344\276\277\344\275\277\347\224\250 `unwrap`.md" new file mode 100644 index 00000000..92b2aa29 --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/G.ERR.01 \345\234\250\345\244\204\347\220\206 `OptionT` \345\222\214 `Result T, E` \347\261\273\345\236\213\346\227\266\357\274\214\344\270\215\350\246\201\351\232\217\344\276\277\344\275\277\347\224\250 `unwrap`.md" @@ -0,0 +1,38 @@ +## G.ERR.01 在处理 `Option` 和 `Result` 类型时,不要随便使用 `unwrap` + +**【级别】** 建议 + +**【描述】** + +当 `Option` 和 `Result`类型的值分别是 `None` 或 `Err` 时,直接对其 `unwrap()` 会导致程序恐慌! + +**【反例】** + +```rust +fn select(opt: Option) { + opt.unwrap(); // 可以用 expect 方法来处理 None 的情况 +} +// OR +fn select(opt: Result) { + res.unwrap(); // 可以用 expect 方法来处理 Err 的情况 +} +``` + +**【正例】** + +```rust +fn select(opt: Option) { + opt.expect("more helpful message"); // 可以用 expect 方法来处理 None 的情况 +} +// OR +fn select(opt: Result) { + res.expect("more helpful message"); // 可以用 expect 方法来处理 Err 的情况 +} +``` + +### 【Lint 检测】 + +| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level | +| ------------------------------------------------------------ | ------------- | ------------ | ----------- | ----- | +| [unwrap_used](https://rust-lang.github.io/rust-clippy/master/#unwrap_used) | yes | no | restriction | allow | + diff --git "a/src/safe-guides/coding_practice/error-handle/G.ERR.02 \344\270\215\350\246\201\346\273\245\347\224\250 `expect`\357\274\214\350\257\267\350\200\203\350\231\221\347\224\250 `unwrap_or_` \347\263\273\345\210\227\346\226\271\346\263\225\344\273\243\346\233\277.md" "b/src/safe-guides/coding_practice/error-handle/G.ERR.02 \344\270\215\350\246\201\346\273\245\347\224\250 `expect`\357\274\214\350\257\267\350\200\203\350\231\221\347\224\250 `unwrap_or_` \347\263\273\345\210\227\346\226\271\346\263\225\344\273\243\346\233\277.md" new file mode 100644 index 00000000..36dc7738 --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/G.ERR.02 \344\270\215\350\246\201\346\273\245\347\224\250 `expect`\357\274\214\350\257\267\350\200\203\350\231\221\347\224\250 `unwrap_or_` \347\263\273\345\210\227\346\226\271\346\263\225\344\273\243\346\233\277.md" @@ -0,0 +1,59 @@ +## G.ERR.02 不要滥用 `expect`,请考虑用 `unwrap_or_` 系列方法代替 + +**【级别】** 建议 + +**【描述】** + +使用 `expect` 的时候请遵循 `expect` 的语义,不要滥用。参考 : **P.ERR.05** 。 + +但是对于一些存在“副作用”的函数,在 遇到 `None` 或 `Err` 时,可能需要返回一些指定的值。这个时候用 `expect` 就不太符合语义。 + +如果你的用法完全符合 `expect` 语义,那么可以设置 `#![allow(clippy::expect_fun_call]` + +**【反例】** + +```rust +let foo = Some(String::new()); +let err_code = "418"; +let err_msg = "I'm a teapot"; +foo.expect(&format!("Err {}: {}", err_code, err_msg)); +// or +foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); +``` + +**【正例】** + +```rust +let foo = Some(String::new()); +let err_code = "418"; +let err_msg = "I'm a teapot"; +foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg)); // 你可以根据场景选择性使用 panic! 或者 不 panic! +``` + +**【例外】** + +完全符合 `expect` 语义的使用。 + +```rust +#![allow(clippy::expect_fun_call] + +// 这个配置文件默认会跟随源码出现,所以,必定可以读取到 +// 这个配置文件不应该没有被提供,如果万一出现了没有提供的情况,需要 Panic 并提供错误信息方便调试,或者让使用者知道原因 +let config = Config::read("some_config.toml").expect("Provide the correct configuration file"); + +// or + +fn main() { + use std::net::IpAddr; + let _home: IpAddr = "127.0.0.1".parse().expect("Provide the correct Ip addr"); +} +``` + + +### 【Lint 检测】 + +| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level | +| ------------------------------------------------------------ | ------------- | ------------ | ----------- | ----- | +| [expect_fun_call](https://rust-lang.github.io/rust-clippy/master/#expect_fun_call) | yes | no | perf | warn | +| [expect_used](https://rust-lang.github.io/rust-clippy/master/#expect_used) | yes | no | restriction | allow | + diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.01 \345\275\223\344\274\240\345\205\245\345\207\275\346\225\260\347\232\204\345\217\202\346\225\260\345\200\274\345\233\240\344\270\272\350\266\205\345\207\272\346\237\220\347\247\215\351\231\220\345\210\266\345\217\257\350\203\275\344\274\232\345\257\274\350\207\264\345\207\275\346\225\260\350\260\203\347\224\250\345\244\261\350\264\245\357\274\214\345\272\224\350\257\245\344\275\277\347\224\250\346\226\255\350\250\200.md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.01 \345\275\223\344\274\240\345\205\245\345\207\275\346\225\260\347\232\204\345\217\202\346\225\260\345\200\274\345\233\240\344\270\272\350\266\205\345\207\272\346\237\220\347\247\215\351\231\220\345\210\266\345\217\257\350\203\275\344\274\232\345\257\274\350\207\264\345\207\275\346\225\260\350\260\203\347\224\250\345\244\261\350\264\245\357\274\214\345\272\224\350\257\245\344\275\277\347\224\250\346\226\255\350\250\200.md" new file mode 100644 index 00000000..7496e532 --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.01 \345\275\223\344\274\240\345\205\245\345\207\275\346\225\260\347\232\204\345\217\202\346\225\260\345\200\274\345\233\240\344\270\272\350\266\205\345\207\272\346\237\220\347\247\215\351\231\220\345\210\266\345\217\257\350\203\275\344\274\232\345\257\274\350\207\264\345\207\275\346\225\260\350\260\203\347\224\250\345\244\261\350\264\245\357\274\214\345\272\224\350\257\245\344\275\277\347\224\250\346\226\255\350\250\200.md" @@ -0,0 +1,35 @@ +## P.ERR.01 当传入函数的参数值因为超出某种限制可能会导致函数调用失败,应该使用断言 + +**【描述】** + +当传入函数的某个参数值可能因为超出某种限制,比如超出数组长度的索引、字符串是否包含某个字符、数组是否为空等,应该使用断言。 + +**【正例】** + +```rust +// From: std::vec::Vec::swap_remove +#[stable(feature = "rust1", since = "1.0.0")] +pub fn swap_remove(&mut self, index: usize) -> T { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("swap_remove index (is {}) should be < len (is {})", index, len); + } + + let len = self.len(); + + if index >= len { + // 此处使用断言方法,虽然不是标准库内置断言宏,但也是一种断言 + assert_failed(index, len); + } + unsafe { + // We replace self[index] with the last element. Note that if the + // bounds check above succeeds there must be a last element (which + // can be self[index] itself). + let last = ptr::read(self.as_ptr().add(len - 1)); + let hole = self.as_mut_ptr().add(index); + self.set_len(len - 1); + ptr::replace(hole, last) + } +} +``` \ No newline at end of file diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.02 \345\275\223\345\207\275\346\225\260\347\232\204\350\277\224\345\233\236\345\200\274\346\210\226\350\200\205\347\273\223\346\236\204\344\275\223\345\255\227\346\256\265\347\232\204\345\200\274\350\257\267\344\275\277\347\224\250 Option T.md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.02 \345\275\223\345\207\275\346\225\260\347\232\204\350\277\224\345\233\236\345\200\274\346\210\226\350\200\205\347\273\223\346\236\204\344\275\223\345\255\227\346\256\265\347\232\204\345\200\274\350\257\267\344\275\277\347\224\250 Option T.md" new file mode 100644 index 00000000..e5c6133b --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.02 \345\275\223\345\207\275\346\225\260\347\232\204\350\277\224\345\233\236\345\200\274\346\210\226\350\200\205\347\273\223\346\236\204\344\275\223\345\255\227\346\256\265\347\232\204\345\200\274\350\257\267\344\275\277\347\224\250 Option T.md" @@ -0,0 +1,28 @@ +## P.ERR.02 当函数的返回值或者结构体字段的值可能为空时,请使用`Option` + +**【描述】** + +在某些其他语言中,如果函数的返回值 或 结构体字段的值 可能为空时,通常会设置一个 “哨兵值(Sentinel Value)” 来应对这种问题,比如使用一个 `nil` 或 `-1` 等特殊值来判断这类情况。 + +但是,在 Rust 中不需要这样,Rust 提供了 `Option` 类型就是专门用于应对这类情况。 + +**【正例】** + +```rust +struct Config { + must: String, + opt: Option, +} + +// OR + +fn main() { + let sentence = "The fox jumps over the dog"; + let index = sentence.find("fox"); + + if let Some(fox) = index { + let words_after_fox = &sentence[fox..]; + println!("{}", words_after_fox); + } +} +``` \ No newline at end of file diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.03 \345\275\223\347\250\213\345\272\217\344\270\255\346\234\211\344\270\215\345\217\257\346\201\242\345\244\215\347\232\204\351\224\231\350\257\257\346\227\266\357\274\214\345\272\224\350\257\245\350\256\251\345\205\266 Panic.md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.03 \345\275\223\347\250\213\345\272\217\344\270\255\346\234\211\344\270\215\345\217\257\346\201\242\345\244\215\347\232\204\351\224\231\350\257\257\346\227\266\357\274\214\345\272\224\350\257\245\350\256\251\345\205\266 Panic.md" new file mode 100644 index 00000000..a4635902 --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.03 \345\275\223\347\250\213\345\272\217\344\270\255\346\234\211\344\270\215\345\217\257\346\201\242\345\244\215\347\232\204\351\224\231\350\257\257\346\227\266\357\274\214\345\272\224\350\257\245\350\256\251\345\205\266 Panic.md" @@ -0,0 +1,19 @@ +## P.ERR.03 当程序中有不可恢复的错误时,应该让其 Panic + +**【描述】** + +如果遇到无法恢复的错误,则需要让程序 Panic。 + +相关 Clippy Lint: [if_then_panic](https://rust-lang.github.io/rust-clippy/master/#if_then_panic) + +**【正例】** + +```rust +fn boot(ptr: *const usize) { + if ptr.is_null() { + panic!("ptr is null! boot failed!") + } + // or + assert!(ptr.is_null(), "ptr is null! boot failed!"); +} +``` \ No newline at end of file diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.04 \345\275\223\347\250\213\345\272\217\344\270\255\351\234\200\350\246\201\345\244\204\347\220\206\351\224\231\350\257\257\345\272\224\350\257\245\344\275\277\347\224\250ResultTE\346\223\215\344\275\234\347\254\246.md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.04 \345\275\223\347\250\213\345\272\217\344\270\255\351\234\200\350\246\201\345\244\204\347\220\206\351\224\231\350\257\257\345\272\224\350\257\245\344\275\277\347\224\250ResultTE\346\223\215\344\275\234\347\254\246.md" new file mode 100644 index 00000000..90fd47da --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.04 \345\275\223\347\250\213\345\272\217\344\270\255\351\234\200\350\246\201\345\244\204\347\220\206\351\224\231\350\257\257\345\272\224\350\257\245\344\275\277\347\224\250ResultTE\346\223\215\344\275\234\347\254\246.md" @@ -0,0 +1,22 @@ +## P.ERR.04 当程序中需要处理错误时,应该使用 `Result` 和 `?` 操作符 + +**【描述】** + +当需要处理错误时,为了保证 程序的健壮性,应该尽可能处理错误。 + +**【反例】** + +在实现原型类项目的时候,可以“快、糙、猛”地使用 `expect` 。但是要进生产环境,需要合理地处理错误。 + +```rust +let res: Result = Ok(1); +res.expect("one"); // 如果有 Err, expect会 Panic ! + +``` + +**【正例】** + +```rust +let res: Result = Ok(1); +res?; // Ok::<(), ()>(()) +``` \ No newline at end of file diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.05 \345\234\250\347\241\256\345\256\232 `OptionT\345\222\214ResultTE\347\261\273\345\236\213\347\232\204\345\200\274\344\270\215\345\217\257\350\203\275\346\230\257None\346\210\226Err\346\227\266\350\257\267\347\224\250 expect \344\273\243\346\233\277unwrap().md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.05 \345\234\250\347\241\256\345\256\232 `OptionT\345\222\214ResultTE\347\261\273\345\236\213\347\232\204\345\200\274\344\270\215\345\217\257\350\203\275\346\230\257None\346\210\226Err\346\227\266\350\257\267\347\224\250 expect \344\273\243\346\233\277unwrap().md" new file mode 100644 index 00000000..9690737f --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.05 \345\234\250\347\241\256\345\256\232 `OptionT\345\222\214ResultTE\347\261\273\345\236\213\347\232\204\345\200\274\344\270\215\345\217\257\350\203\275\346\230\257None\346\210\226Err\346\227\266\350\257\267\347\224\250 expect \344\273\243\346\233\277unwrap().md" @@ -0,0 +1,54 @@ +## P.ERR.05 在确定 `Option` 和 `Result`类型的值不可能是 `None` 或 `Err` 时,请用 `expect` 代替 `unwrap()` + +**【描述】** + +当需要处理的 `Option` 和 `Result` 类型的值,永远都不可能是 `None` 或 `Err` 时,虽然直接 `unwrap()` 也是可以的,但使用 `expect` 会有更加明确的语义。 + +> `expect` 的语义: +> +> 我不打算处理 `None` 或 `Err` 这种可能性,因为我知道这种可能性永远不会发生,或者,它不应该发生。但是 类型系统并不知道它永远不会发生。所以,我需要像类型系统保证,如果它确实发生了,它可以认为是一种错误,并且程序应该崩溃,并带着可以用于跟踪和修复该错误的栈跟踪信息。 + +所以在指定 `expect` 输出消息的时候,请使用肯定的描述,而非否定,用于提升可读性。 + +**【反例】** + +```rust +// 这个配置文件默认会跟随源码出现,所以,必定可以读取到 +// 这个配置文件不应该没有被提供,如果万一出现了没有提供的情况,需要 Panic ,但这里并没有提供错误信息,对于调试或使用都没有帮助 +let config = Config::read("some_config.toml").unwrap(); + +// or +// expect 的输出描述使用否定式内容,可读性不好 +let config = Config::read("some_config.toml").expect("No configuration file provided"); + +// or + +fn main() { + use std::net::IpAddr; + let _home: IpAddr = "127.0.0.1".parse().unwrap(); +} + +// or +// expect 的输出描述使用否定式内容,可读性不好 +fn main() { + use std::net::IpAddr; + let _home: IpAddr = "127.0.0.1".parse().expect("IP addr parse failed!"); +} +``` + +**【正例】** + +```rust +// 这个配置文件默认会跟随源码出现,所以,必定可以读取到 +// 这个配置文件不应该没有被提供,如果万一出现了没有提供的情况,需要 Panic 并提供错误信息方便调试,或者让使用者知道原因 +// expect 里输出的描述信息,使用肯定的内容,整体代码可读性更高,更能突出 expect 的语义 +let config = Config::read("some_config.toml").expect("Provide the correct configuration file"); + +// or + +fn main() { + use std::net::IpAddr; + let _home: IpAddr = "127.0.0.1".parse().expect("Provide the correct Ip addr"); +} +``` + diff --git "a/src/safe-guides/coding_practice/error-handle/P.ERR.06 \346\240\271\346\215\256\345\272\224\347\224\250\350\277\230\346\230\257\345\272\223\346\235\245\351\200\211\346\213\251\344\270\215\345\220\214\347\232\204\351\224\231\350\257\257\345\244\204\347\220\206\346\226\271\345\274\217.md" "b/src/safe-guides/coding_practice/error-handle/P.ERR.06 \346\240\271\346\215\256\345\272\224\347\224\250\350\277\230\346\230\257\345\272\223\346\235\245\351\200\211\346\213\251\344\270\215\345\220\214\347\232\204\351\224\231\350\257\257\345\244\204\347\220\206\346\226\271\345\274\217.md" new file mode 100644 index 00000000..f7c9f130 --- /dev/null +++ "b/src/safe-guides/coding_practice/error-handle/P.ERR.06 \346\240\271\346\215\256\345\272\224\347\224\250\350\277\230\346\230\257\345\272\223\346\235\245\351\200\211\346\213\251\344\270\215\345\220\214\347\232\204\351\224\231\350\257\257\345\244\204\347\220\206\346\226\271\345\274\217.md" @@ -0,0 +1,24 @@ +## P.ERR.06 根据应用还是库来选择不同的错误处理方式 + +**【描述】** + +如果编写应用,建议使用` Error` trait对象;如果编写库,则建议返回自定义错误类型,方便下游处理 + + +**【正例】** + +```rust +// 对于应用使用 Error trait 对象更加方便 +pub fn print(&self, languages: &Languages) -> Result> { + // do something +} + +// 对于库,暴露自定义错误类型更加方便下游处理错误 +#[derive(Debug)] +pub struct SendError(pub T); + +impl fmt::Display for SendError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "channel closed") + } +}