Merge pull request #219 from mg-chao/20220116_translate_errors

[rust-exercise] 翻译 error_handling 部分
pull/220/head
Sunface 3 years ago committed by GitHub
commit 644d6ef86d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,9 +1,10 @@
# Error handling # 错误处理
Most errors arent serious enough to require the program to stop entirely. 大多数的错误并没有严重到需要让程序完全停止运行的程度。
Sometimes, when a function fails, its for a reason that you can easily interpret and respond to. 有时一个函数执行失败时,你可以很容易地对造成失败的原因进行解释并采取对应措施的。
For example, if you try to open a file and that operation fails because the file doesnt exist, you might want to create the file instead of terminating the process. 例如,你正试图打开一个文件,但由于该文件不存在导致了操作失败,这时你可能想创建
该文件而不是直接终止程序。
## Further information ## 更多信息
- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html) - [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html) - [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)

@ -1,18 +1,16 @@
// errors1.rs // errors1.rs
// This function refuses to generate text to be printed on a nametag if // 假使你传给这个函数一个空字符串那么它将拒绝生成一段个性签名nametage
// you pass it an empty string. It'd be nicer if it explained what the problem // 如果它能解释拒绝的原因是什么,而不是粗暴返回 `None` 那就更完美了。
// was, instead of just sometimes returning `None`. The 2nd test currently // 第 2 个测试目前还没通过并未能编译,但它说明了我们希望这个函数具有的行为。
// does not compile or pass, but it illustrates the behavior we would like // 执行 `rustlings hint errors1` 获取提示!
// this function to have.
// Execute `rustlings hint errors1` for hints!
// I AM NOT DONE // I AM NOT DONE
pub fn generate_nametag_text(name: String) -> Option<String> { pub fn generate_nametag_text(name: String) -> Option<String> {// 译:生成个性签名
if name.len() > 0 { if name.len() > 0 {
Some(format!("Hi! My name is {}", name)) Some(format!("Hi! My name is {}", name))// 译:"嗨!我的名字是 {}"
} else { } else {
// Empty names aren't allowed. // 不允许使用空的名字。
None None
} }
} }
@ -21,11 +19,10 @@ pub fn generate_nametag_text(name: String) -> Option<String> {
mod tests { mod tests {
use super::*; use super::*;
// This test passes initially if you comment out the 2nd test. // 你可以注释掉第 2 个测试,那么这个测试就能初步通过。
// You'll need to update what this test expects when you change // 当你更改了测试的函数时,也需要修改下测试代码以使测试正确!
// the function under test!
#[test] #[test]
fn generates_nametag_text_for_a_nonempty_name() { fn generates_nametag_text_for_a_nonempty_name() {// 译:用一个非空名称生成一段个性签名
assert_eq!( assert_eq!(
generate_nametag_text("Beyoncé".into()), generate_nametag_text("Beyoncé".into()),
Some("Hi! My name is Beyoncé".into()) Some("Hi! My name is Beyoncé".into())
@ -33,7 +30,7 @@ mod tests {
} }
#[test] #[test]
fn explains_why_generating_nametag_text_fails() { fn explains_why_generating_nametag_text_fails() {// 译:说明为什么个性签名生成失败了
assert_eq!( assert_eq!(
generate_nametag_text("".into()), generate_nametag_text("".into()),
Err("`name` was empty; it must be nonempty.".into()) Err("`name` was empty; it must be nonempty.".into())

@ -1,20 +1,16 @@
// errors2.rs // errors2.rs
// Say we're writing a game where you can buy items with tokens. All items cost // 假设我们正在编写一个游戏,你可以用代币购买物品。
// 5 tokens, and whenever you purchase items there is a processing fee of 1 // 所有物品的价格都是 5 个代币,每当你购买物品时,都需要 1 个代币的小费。
// token. A player of the game will type in how many items they want to buy, // 游戏玩家将输入他们想要购买的物品数量,`total_cost` 函数能够计算出所需的代币数量。
// and the `total_cost` function will calculate the total number of tokens. // 虽然玩家输入的是数量,但我们得到的却是一个字符串——他们可能输入了任何东西,而不仅仅是数字!
// Since the player typed in the quantity, though, we get it as a string-- and
// they might have typed anything, not just numbers! // 目前这个函数没有处理任何错误的情况(也没有处理成功的情况)。
// 我们要做的是:
// Right now, this function isn't handling the error case at all (and isn't // 如果我们在非数字的字符串上调用 `parse` 方法,该方法将返回 `ParseIntError`
// handling the success case properly either). What we want to do is: // 在这种情况下,我们要立刻从函数返回这个错误,而不是继续进行相关计算。
// if we call the `parse` function on a string that is not a number, that
// function will return a `ParseIntError`, and in that case, we want to // 至少有两种方法可以做到这点,它们都是正确的——但其中一种简短得多!
// immediately return that error from our function and not try to multiply // 执行 `rustlings hint errors2` 以获得关于这两种方式的提示。
// and add.
// There are at least two ways to implement this that are both correct-- but
// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways.
// I AM NOT DONE // I AM NOT DONE
@ -42,6 +38,6 @@ mod tests {
assert_eq!( assert_eq!(
total_cost("beep boop").unwrap_err().to_string(), total_cost("beep boop").unwrap_err().to_string(),
"invalid digit found in string" "invalid digit found in string"
); );// 译:字符串中包含无效的数字
} }
} }

@ -1,8 +1,7 @@
// errors3.rs // errors3.rs
// This is a program that is trying to use a completed version of the // 这是一个试图使用前面练习中 `total_cost` 函数完整版的程序。
// `total_cost` function from the previous exercise. It's not working though! // 但出了些问题!为什么不行?我们需要怎样做才能解决问题?
// Why not? What should we do to fix it? // 执行 `rustlings hint errors3` 获取提示!
// Execute `rustlings hint errors3` for hints!
// I AM NOT DONE // I AM NOT DONE
@ -15,10 +14,10 @@ fn main() {
let cost = total_cost(pretend_user_input)?; let cost = total_cost(pretend_user_input)?;
if cost > tokens { if cost > tokens {
println!("You can't afford that many!"); println!("You can't afford that many!");// 译:你的代币不足以完成支付!
} else { } else {
tokens -= cost; tokens -= cost;
println!("You now have {} tokens.", tokens); println!("You now have {} tokens.", tokens);// 译:现在你有 {} 个代币"
} }
} }

@ -1,5 +1,5 @@
// errors4.rs // errors4.rs
// Make this test pass! Execute `rustlings hint errors4` for hints :) // 通过测试!执行 `rustlings hint errors4` 获取提示 :)
// I AM NOT DONE // I AM NOT DONE

@ -1,8 +1,8 @@
// errors5.rs // errors5.rs
// This program uses a completed version of the code from errors4. // 这个程序使用练习 errors4 代码的完整版。
// It won't compile right now! Why? // 它现在不能编译! 为什么呢?
// Execute `rustlings hint errors5` for hints! // 执行 `rustlings hint errors5` 获取提示!
// I AM NOT DONE // I AM NOT DONE
@ -10,7 +10,7 @@ use std::error;
use std::fmt; use std::fmt;
use std::num::ParseIntError; use std::num::ParseIntError;
// TODO: update the return type of `main()` to make this compile. // TODO:修改 `main()` 的返回类型,以使其通过编译。
fn main() -> Result<(), ParseIntError> { fn main() -> Result<(), ParseIntError> {
let pretend_user_input = "42"; let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?; let x: i64 = pretend_user_input.parse()?;
@ -18,7 +18,7 @@ fn main() -> Result<(), ParseIntError> {
Ok(()) Ok(())
} }
// Don't change anything below this line. // 不要更改此行以下的任何内容。
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64); struct PositiveNonzeroInteger(u64);
@ -39,7 +39,7 @@ impl PositiveNonzeroInteger {
} }
} }
// This is required so that `CreationError` can implement `error::Error`. // 以下是必要的,以便 `CreationError` 能够实现 `error::Error` 。
impl fmt::Display for CreationError { impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self { let description = match *self {

@ -1,18 +1,17 @@
// errors6.rs // errors6.rs
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended // 像 `Box<dyn error::Error>` 这样的万能错误类型并不推荐用于库代码,
// for library code, where callers might want to make decisions based on the // 因为调用者可能想根据错误内容进行相关处理,而不是将其打印出来或进一步传播。
// error content, instead of printing it out or propagating it further. Here, // 在这里,我们自定义了一个错误类型,让调用者有可能去决定当函数返回错误时
// we define a custom error type to make it possible for callers to decide // 下一步该怎么做
// what to do next when our function returns an error.
// Make these tests pass! Execute `rustlings hint errors6` for hints :) // 通过这些测试!执行 `rustlings hint errors6` 获取提示 :)
// I AM NOT DONE // I AM NOT DONE
use std::num::ParseIntError; use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`. // 这是一个自定义的错误类型,我们将在 `parse_pos_nonzero()` 中使用。
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
enum ParsePosNonzeroError { enum ParsePosNonzeroError {
Creation(CreationError), Creation(CreationError),
@ -23,20 +22,19 @@ impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> ParsePosNonzeroError { fn from_creation(err: CreationError) -> ParsePosNonzeroError {
ParsePosNonzeroError::Creation(err) ParsePosNonzeroError::Creation(err)
} }
// TODO: add another error conversion function here. // TODO:在这添加另一个错误转换函数。
} }
fn parse_pos_nonzero(s: &str) fn parse_pos_nonzero(s: &str)
-> Result<PositiveNonzeroInteger, ParsePosNonzeroError> -> Result<PositiveNonzeroInteger, ParsePosNonzeroError>
{ {
// TODO: change this to return an appropriate error instead of panicking // TODO改为返回一个恰当的错误而不是在 `parse()` 返回一个错误时 panic
// when `parse()` returns an error.
let x: i64 = s.parse().unwrap(); let x: i64 = s.parse().unwrap();
PositiveNonzeroInteger::new(x) PositiveNonzeroInteger::new(x)
.map_err(ParsePosNonzeroError::from_creation) .map_err(ParsePosNonzeroError::from_creation)
} }
// Don't change anything below this line. // 不要更改这一行以下的任何内容。
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64); struct PositiveNonzeroInteger(u64);
@ -63,7 +61,7 @@ mod test {
#[test] #[test]
fn test_parse_error() { fn test_parse_error() {
// We can't construct a ParseIntError, so we have to pattern match. // 我们不能构造一个 ParseIntError ,所以必须进行模式匹配。
assert!(matches!( assert!(matches!(
parse_pos_nonzero("not a number"), parse_pos_nonzero("not a number"),
Err(ParsePosNonzeroError::ParseInt(_)) Err(ParsePosNonzeroError::ParseInt(_))

@ -433,74 +433,63 @@ name = "errors1"
path = "exercises/error_handling/errors1.rs" path = "exercises/error_handling/errors1.rs"
mode = "test" mode = "test"
hint = """ hint = """
`Err` is one of the variants of `Result`, so what the 2nd test is saying `Err` `Result` `generate_nametag_text`
is that `generate_nametag_text` should return a `Result` instead of an `Result` `Option`
`Option`.
To make this change, you'll need to:
- update the return type in the function signature to be a Result<String, String> that - Result<String, String>便 `Ok(String)` `Err(String)`
could be the variants `Ok(String)` and `Err(String)` - `Some(stuff)` `Ok(stuff)`
- change the body of the function to return `Ok(stuff)` where it currently - `None` `Err(error message)`
returns `Some(stuff)` - `Some(stuff)` `Ok(stuff)`"""
- change the body of the function to return `Err(error message)` where it
currently returns `None`
- change the first test to expect `Ok(stuff)` where it currently expects
`Some(stuff)`."""
[[exercises]] [[exercises]]
name = "errors2" name = "errors2"
path = "exercises/error_handling/errors2.rs" path = "exercises/error_handling/errors2.rs"
mode = "test" mode = "test"
hint = """ hint = """
One way to handle this is using a `match` statement on `item_quantity.parse::<i32>()` 使 match
`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and `Ok(something)` `Err(something)`
`Err(something)`. This pattern is very common in Rust, though, so there's Rust `?`
a `?` operator that does pretty much what you would make that match statement Error Handling :
do for you! Take a look at this section of the Error Handling chapter:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
and give it a try!""" """
[[exercises]] [[exercises]]
name = "errors3" name = "errors3"
path = "exercises/error_handling/errors3.rs" path = "exercises/error_handling/errors3.rs"
mode = "compile" mode = "compile"
hint = """ hint = """
If other functions can return a `Result`, why shouldn't `main`?""" `Result` `main` """
[[exercises]] [[exercises]]
name = "errors4" name = "errors4"
path = "exercises/error_handling/errors4.rs" path = "exercises/error_handling/errors4.rs"
mode = "test" mode = "test"
hint = """ hint = """
`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. `PositiveNonzeroInteger::new` `Ok`
It should be doing some checking, returning an `Err` result if those checks fail, and only `Err` `Ok` :)"""
returning an `Ok` result if those checks determine that everything is... okay :)"""
[[exercises]] [[exercises]]
name = "errors5" name = "errors5"
path = "exercises/error_handling/errors5.rs" path = "exercises/error_handling/errors5.rs"
mode = "compile" mode = "compile"
hint = """ hint = """
Hint: There are two different possible `Result` types produced within `main()` `Result` `?`
`main()`, which are propagated using `?` operators. How do we declare a `main()`
return type from `main()` that allows both?
Another hint: under the hood, the `?` operator calls `From::from` `?` `From::from`
on the error value to convert it to a boxed trait object, a `Box<dyn error::Error>`
`Box<dyn error::Error>`, which is polymorphic-- that means that lots of `error::Error`
different kinds of errors can be returned from the same function because
all errors act the same since they all implement the `error::Error` trait.
Check out this section of the book:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
This exercise uses some concepts that we won't get to until later in the 使 `Box` `From`
course, like `Box` and the `From` trait. It's not important to understand
them in detail right now, but you can read ahead if you like.
Read more about boxing errors: boxing errors
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
Read more about using the `?` operator with boxed errors: 使 `?`
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
""" """
@ -509,19 +498,15 @@ name = "errors6"
path = "exercises/error_handling/errors6.rs" path = "exercises/error_handling/errors6.rs"
mode = "test" mode = "test"
hint = """ hint = """
This exercise uses a completed version of `PositiveNonzeroInteger` from 使 error 4 `PositiveNonzeroInteger`
errors4.
Below the line that TODO asks you to change, there is an example of using TODO `Result` 使 `map_err()`
the `map_err()` method on a `Result` to transform one type of error into `parse()` `Result` 使西
another. Try using something similar on the `Result` from `parse()`. You 使 `?` 使 `match`
might use the `?` operator to return early from the function, or you might
use a `match` expression, or maybe there's another way!
You can create another function inside `impl ParsePosNonzeroError` to use `impl ParsePosNonzeroError` `map_err()` 使
with `map_err()`.
Read more about `map_err()` in the `std::result` documentation: `std::result` `map_err()`
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
# Generics # Generics

Loading…
Cancel
Save