diff --git a/README.md b/README.md index 0500dcada9..ba7ec0515b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ PR 리뷰를 통해 통합하고, 그 외에 오탈자 수정 등 자잘한 수 ### 현재까지 번역 용어 정리 (abc순) +- arm: (match 문에서의 arm) 갈래 - assign: 대입하다 - associated function: 연관함수 - binary: 바이너리 @@ -75,6 +76,7 @@ PR 리뷰를 통해 통합하고, 그 외에 오탈자 수정 등 자잘한 수 - pointer: 포인터 - private: 비공개 - public: 공개 +- recover: 복구 - reference: 참조자 - return: 반환 - rust: 러스트 diff --git a/second-edition/src/SUMMARY.md b/second-edition/src/SUMMARY.md index 4b36feef7c..516f37fa7f 100644 --- a/second-edition/src/SUMMARY.md +++ b/second-edition/src/SUMMARY.md @@ -42,10 +42,10 @@ - [스트링](ch08-02-strings.md) - [해쉬맵](ch08-03-hash-maps.md) -- [Error Handling](ch09-00-error-handling.md) - - [Unrecoverable Errors with `panic!`](ch09-01-unrecoverable-errors-with-panic.md) - - [Recoverable Errors with `Result`](ch09-02-recoverable-errors-with-result.md) - - [To `panic!` or Not To `panic!`](ch09-03-to-panic-or-not-to-panic.md) +- [에러 처리](ch09-00-error-handling.md) + - [`panic!`과 함께하는 복구 불가능한 에러](ch09-01-unrecoverable-errors-with-panic.md) + - [`Result`와 함께하는 복구 가능한 에러](ch09-02-recoverable-errors-with-result.md) + - [`panic!`이냐, `panic!`이 아니냐, 그것이 문제로다](ch09-03-to-panic-or-not-to-panic.md) - [Generic Types, Traits, and Lifetimes](ch10-00-generics.md) - [Generic Data Types](ch10-01-syntax.md) diff --git a/second-edition/src/ch09-00-error-handling.md b/second-edition/src/ch09-00-error-handling.md index 3fbaa31515..165100ae07 100644 --- a/second-edition/src/ch09-00-error-handling.md +++ b/second-edition/src/ch09-00-error-handling.md @@ -1,23 +1,20 @@ -# Error Handling +# 에러 처리 -Rust’s commitment to reliability extends to error handling. Errors are a fact -of life in software, so Rust has a number of features for handling situations -in which something goes wrong. In many cases, Rust will require you to -acknowledge the possibility of an error occurring and take some action before -your code will compile. This makes your program more robust by ensuring that -you won’t only discover errors after you’ve deployed your code to production. +러스트의 신뢰성에 대한 약속은 에러 처리에도 확장되어 있습니다. 에러는 소프트웨어에서 피할 수 없는 현실이며, +따라서 러스트는 무언가 잘못되었을 경우에 대한 처리를 위한 몇가지 기능을 갖추고 있습니다. 많은 경우, +러스트는 여러분이 에러가 발생할 가능성을 인정하고 여러분의 코드가 컴파일 되기 전에 어떤 행동을 취하기를 +요구할 것입니다. 이는 여러분의 코드를 제품으로서 배포한 이후에만 에러를 발견하진 않을 것이라고 보장함으로써 +여러분의 프로그램을 더 강건하게 해줍니다. -Rust groups errors into two major categories: *recoverable* and *unrecoverable* -errors. Recoverable errors are situations when it’s usually reasonable to -report the problem to the user and retry the operation, like a file not being -found. Unrecoverable errors are always symptoms of bugs, like trying to access -a location beyond the end of an array. +러스트는 에러를 두 가지 범주로 묶습니다: *복구 가능한(recoverable)* 에러와 +*복구 불가능한(unrecoverable)* 에러입니다. 복구 가능한 에러는 사용자에게 문제를 보고하고 연산을 +재시도하는 것이 보통 합리적인 경우인데, 이를테면 파일을 찾을 수 없는 등의 경우가 그렇습니다. 복구 불가능한 +에러는 언제나 버그의 증상이 나타나는데, 예를 들면 배열의 끝을 넘어선 위치의 값을 접근하려고 시도하는 경우가 +그렇습니다. -Most languages don’t distinguish between the two kinds of errors, and handle -both in the same way using mechanisms like exceptions. Rust doesn’t have -exceptions. Instead, it has the value `Result` for recoverable errors and -the `panic!` macro that stops execution when it encounters unrecoverable -errors. This chapter will cover calling `panic!` first, then talk about -returning `Result` values. Finally, we’ll discuss considerations to take -into account when deciding whether to try to recover from an error or to stop -execution. +대부분의 언어들은 이 두 종류의 에러를 분간하지 않으며, 예외 처리(exception)와 같은 메카니즘을 +이용하여 같은 방식으로 둘 다 처리합니다. 러스트는 예외 처리 기능이 없습니다. 대신, 복구 가능한 에러를 +위한 `Result` 값과 복구 불가능한 에러가 발생했을 때 실행을 멈추는 `panic!` 매크로를 가지고 +있습니다. 이번 장에서는 `panic!`을 호출하는 것을 먼저 다룬 뒤, `Result` 값을 반환하는 +것에 대해 이야기 하겠습니다. 마지막으로, 에러로부터 복구을 시도할지 아니면 실행을 멈출지를 결정할 때 +고려할 것에 대해 논의해 보겠습니다. diff --git a/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md b/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md index 895f9e15fa..d56546a731 100644 --- a/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md +++ b/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md @@ -1,30 +1,27 @@ -## Unrecoverable Errors with `panic!` +## `panic!`과 함께하는 복구 불가능한 에러 -Sometimes, bad things happen, and there’s nothing that you can do about it. For -these cases, Rust has the `panic!` macro. When this macro executes, your -program will print a failure message, unwind and clean up the stack, and then -quit. The most common situation this occurs in is when a bug of some kind has -been detected and it’s not clear to the programmer how to handle the error. +가끔씩 나쁜 일은 일어나고, 이에 대해 여러분이 할 수 있는 것이 없을 수도 있습니다. 이러한 경우를 위하여 +러스트는 `panic!` 매크로를 가지고 있습니다. 이 매크로가 실행되면, 여러분의 프로그램은 실패 메세지를 +출력하고, 스택을 되감고 청소하고, 그후 종료됩니다. 이런 일이 발생하는 가장 흔한 상황은 어떤 종류의 +버그가 발견되었고 프로그래머가 이 에러를 어떻게 처리할지가 명확하지 않을 때 입니다. -> ### Unwinding the Stack Versus Aborting on Panic +> ### 패닉 상에서 스택 되감기 v.s. 그만두기 > -> By default, when a `panic!` occurs, the program starts -> *unwinding*, which means Rust walks back up the stack and cleans up the data -> from each function it encounters, but this walking and cleanup is a lot of -> work. The alternative is to immediately *abort*, which ends the program -> without cleaning up. Memory that the program was using will then need to be -> cleaned up by the operating system. If in your project you need to make the -> resulting binary as small as possible, you can switch from unwinding to -> aborting on panic by adding `panic = 'abort'` to the appropriate `[profile]` -> sections in your *Cargo.toml*. For example, if you want to abort on panic in -> release mode: +> 기본적으로, `panic!`이 발생하면, 프로그램은 *되감기(unwinding)* 를 시작하는데, 이는 러스트가 +> 패닉을 마주친 각 함수로부터 스택을 거꾸로 훑어가면서 데이터를 제거한다는 뜻이지만, 이 훑어가기 및 +> 제거는 일이 많습니다. 다른 대안으로는 즉시 *그만두기(abort)* 가 있는데, 이는 데이터 제거 없이 +> 프로그램을 끝내는 것입니다. 프로그램이 사용하고 있던 메모리는 운영체제에 의해 청소될 필요가 있을 +> 것입니다. 여러분의 프로젝트 내에서 결과 바이너리가 가능한 작아지기를 원한다면, 여러분의 +> *Cargo.toml* 내에서 적합한 `[profile]` 섹션에 `panic = 'abort'`를 추가함으로써 되감기를 +> 그만두기로 바꿀 수 있습니다. 예를 들면, 여러분이 릴리즈 모드 내에서는 패닉 상에서 그만두기를 +> 쓰고 싶다면 아래와 같이 합니다: > > ```toml > [profile.release] > panic = 'abort' > ``` -Let’s try calling `panic!` with a simple program: +단순한 프로그램으로 `panic!` 호출을 시도해 봅시다: Filename: src/main.rs @@ -34,7 +31,7 @@ fn main() { } ``` -If you run it, you’ll see something like this: +이걸 실행하면 다음과 같은 것을 보게 될 것입니다: ```text $ cargo run @@ -46,23 +43,20 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace. error: Process didn't exit successfully: `target/debug/panic` (exit code: 101) ``` -The last three lines contain the error message caused by the call to `panic!`. -The first line shows our panic message and the place in our source code where -the panic occurred: *src/main.rs:2* indicates that it’s the second line of our -*src/main.rs* file. +마지막 세 줄이 `panic!`을 호출함으로 인해 생긴 에러 메세지를 담고 있습니다. 첫번째 줄은 우리의 패닉 +메세지와 소스 코드에서 패닉이 발생한 지점을 보여줍니다: *src/main.rs:2*는 *src/main.rs* 파일의 +두번째 줄을 가리킵니다. -In this case, the line indicated is part of our code, and if we go to that line -we see the `panic!` macro call. In other cases, the `panic!` call might be in -code that our code calls. The filename and line number reported by the error -message will be someone else’s code where the `panic!` macro is called, not the -line of our code that eventually led to the `panic!`. We can use the backtrace -of the functions the `panic!` call came from to figure this out. +위 예제의 경우, 가리키고 있는 줄은 우리 코드 부분이고, 해당 줄로 가면 `panic!` 매크로 호출을 보게 됩니다. +그 외의 경우들에서는, `panic!` 호출이 우리 코드가 호출한 코드 내에 있을 수도 있습니다. 에러 메세지에 +의해 보고되는 파일 이름과 라인 번호는 `panic!` 매크로가 호출된 다른 누군가의 코드일 것이며, 궁극적으로 +`panic!`을 이끌어낸 것이 우리 코드 라인이 아닐 것입니다. 이를 발견하기 위해서 `panic!` 호출이 +발생된 함수에 대한 백트레이스(backtrace)를 사용할 수 있습니다. -### Using a `panic!` Backtrace +### `panic!` 백트레이스 사용하기 -Let’s look at another example to see what it’s like when a `panic!` call comes -from a library because of a bug in our code instead of from our code calling -the macro directly: +다른 예를 통해서, 우리 코드가 직접 매크로를 호출하는 대신 우리 코드의 버그 때문에 `panic!` 호출이 +라이브러리로부터 발생될 때는 어떻게 되는지 살펴봅시다: Filename: src/main.rs @@ -74,22 +68,18 @@ fn main() { } ``` -We’re attempting to access the hundredth element of our vector, but it only has -three elements. In this situation, Rust will panic. Using `[]` is supposed to -return an element, but if you pass an invalid index, there’s no element that -Rust could return here that would be correct. +우리는 벡터의 100번째 요소에 접근하기를 시도하고 있지만, 벡터는 오직 3개의 요소만 가지고 있습니다. +이러한 상황이면 러스트는 패닉을 일으킬 것입니다. `[]`를 사용하는 것은 어떤 요소를 반환하기를 가정하지만, +유효하지 않은 인덱스를 넘기게 되면 러스트가 반환할 올바른 요소는 없습니다. -Other languages like C will attempt to give you exactly what you asked for in -this situation, even though it isn’t what you want: you’ll get whatever is at -the location in memory that would correspond to that element in the vector, -even though the memory doesn’t belong to the vector. This is called a *buffer -overread*, and can lead to security vulnerabilities if an attacker can -manipulate the index in such a way as to read data they shouldn’t be allowed to -that is stored after the array. +이러한 상황에서 C 같은 다른 언어들은 여러분이 원하는 것이 아닐지라도, 여러분이 요청한 것을 정확히 주려고 +시도할 것입니다: 여러분은 벡터 내에 해당 요소와 상응하는 위치의 메모리에 들어있는 무언가를 얻을 것입니다. +설령 그 메모리 영역이 벡터 소유가 아닐지라도 말이죠. 이러한 것을 *버퍼 오버리드(buffer overread)* +라고 부르며, 만일 어떤 공격자가 읽도록 허용되어선 안되지만 배열 뒤에 저장되어 있는 데이터를 읽어낼 +방법으로서 인덱스를 다룰 수 있게 된다면, 이는 보안 취약점을 발생시킬 수 있습니다. -In order to protect your program from this sort of vulnerability, if you try to -read an element at an index that doesn’t exist, Rust will stop execution and -refuse to continue. Let’s try it and see: +여러분의 프로그램을 이러한 종류의 취약점으로부터 보호하기 위해서, 여러분이 존재하지 않는 인덱스 상의 +요소를 읽으려 시도한다면, 려스트는 실행을 멈추고 계속하기를 거부할 것입니다. 한번 시도해 봅시다: ```text $ cargo run @@ -102,14 +92,12 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace. error: Process didn't exit successfully: `target/debug/panic` (exit code: 101) ``` -This points at a file we didn’t write, *libcollections/vec.rs*. That’s the -implementation of `Vec` in the standard library. The code that gets run when -we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is where -the `panic!` is actually happening. +위 에러는 우리가 작성하지 않은 파일인 *libcollections/vec.rs*를 가리키고 있습니다. 이는 +표준 라이브러리 내에 있는 `Vec`의 구현 부분입니다. 우리가 벡터 `v`에 `[]`를 사용할 때 실행되는 +코드는 *libcollections/vec.rs* 안에 있으며, 그곳이 바로 `panic!`이 실제 발생한 곳입니다. -The next note line tells us that we can set the `RUST_BACKTRACE` environment -variable to get a backtrace of exactly what happened to cause the error. Let’s -try that. Listing 9-1 shows the output: +그 다음 노트는 `RUST_BACKTRACE` 환경 변수를 설정하여 에러의 원인이 된 것이 무엇인지 정확하게 +백트레이스할 수 있다고 말해주고 있습니다. 이를 시도해봅시다. Listing 9-1은 결과를 보여줍니다: ```text $ RUST_BACKTRACE=1 cargo run @@ -151,29 +139,24 @@ stack backtrace: 17: 0x0 - ``` -Listing 9-1: The backtrace generated by a call to -`panic!` displayed when the environment variable `RUST_BACKTRACE` is set - -That’s a lot of output! Line 11 of the backtrace points to the line in our -project causing the problem: *src/main.rs*, line four. A backtrace is a list of -all the functions that have been called to get to this point. Backtraces in -Rust work like they do in other languages: the key to reading the backtrace is -to start from the top and read until you see files you wrote. That’s the spot -where the problem originated. The lines above the lines mentioning your files -are code that your code called; the lines below are code that called your code. -These lines might include core Rust code, standard library code, or crates that -you’re using. - -If we don’t want our program to panic, the location pointed to by the first -line mentioning a file we wrote is where we should start investigating in order -to figure out how we got to this location with values that caused the panic. In -our example where we deliberately wrote code that would panic in order to -demonstrate how to use backtraces, the way to fix the panic is to not try to -request an element at index 100 from a vector that only contains three items. -When your code panics in the future, you’ll need to figure out for your -particular case what action the code is taking with what values that causes the -panic and what the code should do instead. - -We’ll come back to `panic!` and when we should and should not use these methods -later in the chapter. Next, we’ll now look at how to recover from an error with -`Result`. +Listing 9-1: 환경 변수 `RUST_BACKTRACE`가 설정되었을 때 `panic!`의 +호출에 의해 발생되는 백트레이스 출력 + +출력이 엄청 많군요! 백트레이스의 11번 라인이 문제를 일으킨 우리 프로젝트의 라인을 가리키고 있습니다: +*src/main.rs*, 4번 라인입니다. 백트레이스는 이 지점에서 호출되었던 모든 함수들의 리스트입니다. +러스트의 백트레이스는 다른 언어 내에서의 백트레이스와 비슷하게 동작합니다: 백트레이스를 읽는 열쇠는 +여러분이 작성한 파일을 볼때까지 위에서부터 읽어내려가기 시작하는 것입니다. 그곳이 바로 문제가 시작된 +지점입니다. 여러분의 파일이 언급된 라인의 윗 라인들은 여러분의 코드가 호출한 코드입니다; 밑의 라인들은 +여러분의 코드를 호출한 코드입니다. 이 라인들은 핵심 러스트 코드, 표준 라이브러리 코드, 혹은 여러분이 +사용중인 크레이트를 포함할 수도 있습니다. + +만일 프로그램이 패닉에 빠지지 않도록 하고 싶다면, 우리가 작성한 파일이 언급된 첫 라인으로 지적된 위치가 +바로 패닉을 일으킨 값을 가지고 있는 위치를 찾아내기 위해 수사하기 시작할 지점입니다. 백트레이스를 어떻게 +사용하는지 시범을 보이기 위해 고의로 패닉을 일으키는 코드를 작성한 우리의 예제에서, 패닉을 고칠 방법은 +고작 3개의 아이템을 가진 벡터로부터 인덱스 100에서의 요소를 요청하지 않도록 하는 것입니다. 여러분의 +코드가 추후 패닉에 빠졌을 때, 여러분의 특정한 경우에 대하여 어떤 코드가 패닉을 일으키는 값을 만드는지와 +코드는 대신 어떻게 되어야 할지를 알아낼 필요가 있을 것입니다. + +우리는 `panic!`으로 다시 돌아올 것이며 언제 이 메소드를 써야하는지, 혹은 쓰지 말아야 하는지에 대해 +이 장의 뒷 부분에서 알아보겠습니다. 다음으로 `Result`를 이용하여 에러로부터 어떻게 복구하는지를 +보겠습니다. diff --git a/second-edition/src/ch09-02-recoverable-errors-with-result.md b/second-edition/src/ch09-02-recoverable-errors-with-result.md index 17a47b5b19..b196f86771 100644 --- a/second-edition/src/ch09-02-recoverable-errors-with-result.md +++ b/second-edition/src/ch09-02-recoverable-errors-with-result.md @@ -1,14 +1,13 @@ -## Recoverable Errors with `Result` +## `Result`와 함께하는 복구 가능한 에러 -Most errors aren’t serious enough to require the program to stop entirely. -Sometimes, when a function fails, it’s for a reason that we can easily -interpret and respond to. For example, if we try to open a file and that -operation fails because the file doesn’t exist, we might want to create the -file instead of terminating the process. +대부분의 에러는 프로그램을 전부 멈추도록 요구될 정도로 심각하지는 않습니다. 종종 어떤 함수가 실패할 +때는, 우리가 쉽게 해석하고 대응할 수 있는 이유에 대한 것입니다. 예를 들어, 만일 우리가 어떤 파일을 +여는데 해당 파일이 존재하지 않아서 연산에 실패했다면, 프로세스를 멈추는 대신 파일을 새로 만드는 +것을 원할지도 모릅니다. -Recall from Chapter 2 the section on “[Handling Potential Failure with the -`Result` Type][handle_failure]” that the `Result` enum is defined -as having two variants, `Ok` and `Err`, as follows: +2장의 “[`Result` 타입으로 잠재된 실패 다루기][handle_failure]”에서 +`Result` 열거형은 다음과 같이 `Ok`와 `Err`라는 두 개의 variant를 갖도록 정의되어 있음을 +상기하세요: [handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type @@ -19,17 +18,14 @@ enum Result { } ``` -The `T` and `E` are generic type parameters; we’ll go into generics in more -detail in Chapter 10. What you need to know right now is that `T` represents -the type of the value that will be returned in a success case within the `Ok` -variant, and `E` represents the type of the error that will be returned in a -failure case within the `Err` variant. Because `Result` has these generic type -parameters, we can use the `Result` type and the functions that the standard -library has defined on it in many different situations where the successful -value and error value we want to return may differ. +`T`와 `E`는 제네릭 타입 파라미터입니다; 10장에서 제네릭에 대해 더 자세히 다룰 것입니다. 지금으로서 +여러분이 알아둘 필요가 있는 것은, `T`는 성공한 경우에 `Ok` variant 내에 반환될 값의 타입을 나타내고 +`E`는 실패한 경우에 `Err` variant 내에 반환될 에러의 타입을 나타내는 것이라는 점입니다. `Result`가 +이러한 제네릭 타입 파라미터를 갖기 때문에, 우리가 반환하고자 하는 성공적인 값과 에러 값이 다를 수 있는 +다양한 상황 내에서 표준 라이브러리에 정의된 `Result` 타입과 함수들을 사용할 수 있습니다. -Let’s call a function that returns a `Result` value because the function could -fail: opening a file, shown in Listing 9-2. +Listing 9-2에서 보시는 것 처럼 파일을 열 때 함수가 실패할 수도 있어 `Result` 값을 반환하는 함수를 +호출해 봅시다: Filename: src/main.rs @@ -41,21 +37,20 @@ fn main() { } ``` -Listing 9-2: Opening a file +Listing 9-2: 파일 열기 -How do we know `File::open` returns a `Result`? We could look at the standard -library API documentation, or we could ask the compiler! If we give `f` a type -annotation of some type that we know the return type of the function is *not*, -then we try to compile the code, the compiler will tell us that the types don’t -match. The error message will then tell us what the type of `f` *is*! Let’s try -it: we know that the return type of `File::open` isn’t of type `u32`, so let’s -change the `let f` statement to: +`File::open`이 `Result`를 반환하는지 어떻게 알까요? 표준 라이브러리 API 문서를 찾아보거나, +컴파일러에게 물어볼 수 있습니다! 만일 `f`에게 우리가 알고 있고 그 함수의 반환 타입은 *아닐* 어떤 타입에 +대한 타입 명시를 주고, 그리고 그 코드의 컴파일을 시도한다면, 컴파일러는 우리에게 타입이 맞지 않는다고 +알려줄 것입니다. 그후 에러 메세지는 `f`의 타입이 *무엇인지* 알려줄 것입니다! 한번 해봅시다: +우리는 `File::open`의 반환 타입이 `u32`는 아니라는 것을 알고 있으니, `let f` 구문을 이렇게 +바꿔봅시다: ```rust,ignore let f: u32 = File::open("hello.txt"); ``` -Attempting to compile now gives us: +이제 컴파일을 시도하면 다음 메세지가 나타납니다: ```text error[E0308]: mismatched types @@ -69,28 +64,23 @@ error[E0308]: mismatched types = note: found type `std::result::Result` ``` -This tells us the return type of the `File::open` function is a `Result`. -The generic parameter `T` has been filled in here with the type of the success -value, `std::fs::File`, which is a file handle. The type of `E` used in the -error value is `std::io::Error`. - -This return type means the call to `File::open` might succeed and return to us -a file handle that we can read from or write to. The function call also might -fail: for example, the file might not exist, or we might not have permission to -access the file. The `File::open` function needs to have a way to tell us -whether it succeeded or failed, and at the same time give us either the file -handle or error information. This information is exactly what the `Result` enum -conveys. - -In the case where `File::open` succeeds, the value we will have in the variable -`f` will be an instance of `Ok` that contains a file handle. In the case where -it fails, the value in `f` will be an instance of `Err` that contains more -information about the kind of error that happened. - -We need to add to the code from Listing 9-2 to take different actions depending -on the value `File::open` returned. Listing 9-3 shows one way to handle the -`Result` with a basic tool: the `match` expression that we learned about in -Chapter 6. +이 메세지는 `File::open` 함수의 반환 타입이 `Result`라는 것을 알려줍니다. 여기서 제네릭 +파라미터 `T`는 성공값의 타입인 `std::fs::File`로 체워져 있는데, 이것은 파일 핸들입니다. 에러에 +사용되는 `E`의 타입은 `std::io::Error`입니다. + +이 반환 타입은 `File::open`을 호출하는 것이 성공하여 우리가 읽거나 쓸 수 있는 파일 핸들을 반환해 +줄 수도 있다는 뜻입니다. 함수 호출은 또한 실패할 수도 있습니다: 예를 들면 파일이 존재하지 않을지도 +모르고, 혹은 파일에 접근할 권한이 없을지도 모릅니다. `File::open` 함수는 우리에게 성공했는지 혹은 +실패했는지를 알려주고, 동시에 파일 핸들이나 에러 정보 둘 중 하나를 우리에게 제공할 방법을 가질 필요가 +있습니다. 바로 이러한 정보가 `Result` 열거형이 전달하는 것과 정확히 일치합니다. + +`File::open`이 성공한 경우, 변수 `f`가 가지게 될 값은 파일 핸들을 담고 있는 `Ok` 인스턴스가 +될 것입니다. 실패한 경우, `f`의 값은 발생한 에러의 종류에 대한 더 많은 정보를 가지고 있는 `Err`의 +인스턴스가 될 것입니다. + +우리는 Listing 9-2의 코드에 `File::open`이 반환하는 값에 따라 다른 행동을 취하는 코드를 추가할 +필요가 있습니다. Listing 9-3은 우리가 6장에서 배웠던 기초 도구 `match` 표현식을 이용하여 +`Result`를 처리하는 한 가지 방법을 보여줍니다: Filename: src/main.rs @@ -109,38 +99,34 @@ fn main() { } ``` -Listing 9-3: Using a `match` expression to handle the -`Result` variants we might have +Listing 9-3: `match` 표현식을 사용하여 발생 가능한 `Result` +variant들을 처리하기 -Note that, like the `Option` enum, the `Result` enum and its variants have been -imported in the prelude, so we don’t need to specify `Result::` before the `Ok` -and `Err` variants in the `match` arms. +`Option` 열거형과 같이 `Result` 열거형과 variant들은 프렐루드(prelude)로부터 가져와진다는 점을 +기억하세요. 따라서 `match`의 각 경우에 대해서 `Ok`와 `Err` 앞에 `Result::`를 특정하지 않아도 +됩니다. -Here we tell Rust that when the result is `Ok`, return the inner `file` value -out of the `Ok` variant, and we then assign that file handle value to the -variable `f`. After the `match`, we can then use the file handle for reading or -writing. +여기서 우리는 러스트에게 결과가 `Ok`일 때에는 `Ok` variant로부터 내부의 `file` 값을 반환하고, +이 파일 핸들 값을 변수 `f`에 대입한다고 말해주고 있습니다. `match` 이후에는 읽거나 쓰기 위해 +이 파일 핸들을 사용할 수 있습니다. -The other arm of the `match` handles the case where we get an `Err` value from -`File::open`. In this example, we’ve chosen to call the `panic!` macro. If -there’s no file named *hello.txt* in our current directory and we run this -code, we’ll see the following output from the `panic!` macro: +`match`의 다른 경우는 `File::open`으로부터 `Err`를 얻은 경우를 처리합니다. 이 예제에서는 +`panic!` 매크로를 호출하는 방법을 택했습니다. 우리의 현재 디렉토리 내에 *hello.txt*라는 이름의 +파일이 없는데 이 코드를 실행하게 되면, `panic!` 매크로로부터 다음과 같은 출력을 보게 될 것입니다: ```text thread 'main' panicked at 'There was a problem opening the file: Error { repr: Os { code: 2, message: "No such file or directory" } }', src/main.rs:8 ``` -### Matching on Different Errors +### 서로 다른 에러에 대해 매칭하기 -The code in Listing 9-3 will `panic!` no matter the reason that `File::open` -failed. What we’d really like to do instead is take different actions for -different failure reasons: if `File::open` failed because the file doesn’t -exist, we want to create the file and return the handle to the new file. If -`File::open` failed for any other reason, for example because we didn’t have -permission to open the file, we still want to `panic!` in the same way as we -did in Listing 9-3. Let’s look at Listing 9-4, which adds another arm to the -`match`: +Listing 9-3의 코드는 `File::open`이 실패한 이유가 무엇이든 간에 `panic!`을 일으킬 것입니다. +대신 우리가 진짜 하고 싶은 것은 실패 이유에 따라 다른 행동을 취하는 것입니다: 파일이 없어서 +`File::open`이 실패한 것이라면, 새로운 파일을 만들어서 핸들을 반환하고 싶습니다. 만일 그밖의 +이유로 `File::open`이 실패한 거라면, 예를 들어 파일을 열 권한이 없어서라면, Listing 9-3에서 +했던 것과 마찬가지로 `panic!`을 일으키고 싶습니다. `match`에 새로운 경우를 추가한 Listing 9-4를 +봅시다: Filename: src/main.rs @@ -174,44 +160,36 @@ fn main() { } ``` -Listing 9-4: Handling different kinds of errors in -different ways - -The type of the value that `File::open` returns inside the `Err` variant is -`io::Error`, which is a struct provided by the standard library. This struct -has a method `kind` that we can call to get an `io::ErrorKind` value. -`io::ErrorKind` is an enum provided by the standard library that has variants -representing the different kinds of errors that might result from an `io` -operation. The variant we’re interested in is `ErrorKind::NotFound`, which -indicates the file we’re trying to open doesn’t exist yet. - -The condition `if error.kind() == ErrorKind::NotFound` is called a *match -guard*: it’s an extra condition on a `match` arm that further refines the arm’s -pattern. This condition must be true in order for that arm’s code to get run; -otherwise, the pattern matching will move on to consider the next arm in the -`match`. The `ref` in the pattern is needed so that `error` is not moved into -the guard condition but is merely referenced by it. The reason `ref` is used to -take a reference in a pattern instead of `&` will be covered in detail in -Chapter 18. In short, in the context of a pattern, `&` matches a reference and -gives us its value, but `ref` matches a value and gives us a reference to it. - -The condition we want to check in the match guard is whether the value returned -by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is, -we try to create the file with `File::create`. However, since `File::create` -could also fail, we need to add an inner `match` statement as well! When the -file can’t be opened, a different error message will be printed. The last arm -of the outer `match` stays the same so that the program panics on any error -besides the missing file error. - -### Shortcuts for Panic on Error: `unwrap` and `expect` - -Using `match` works well enough, but it can be a bit verbose and doesn’t always -communicate intent well. The `Result` type has many helper methods -defined on it to do various things. One of those methods, called `unwrap`, is a -shortcut method that is implemented just like the `match` statement we wrote in -Listing 9-3. If the `Result` value is the `Ok` variant, `unwrap` will return -the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will -call the `panic!` macro for us. +Listing 9-4: 다른 종류의 에러를 다른 방식으로 처리하기 + +`Err` variant 내에 있는 `File::open`이 반환하는 값의 타입은 `io::Error`인데, 이는 +표준 라이브러리에서 제공하는 구조체입니다. 이 구조체는 `kind` 메소드를 제공하는데 이를 호출하여 +`io::ErrorKind`값을 얻을 수 있습니다. `io::ErrorKind`는 `io` 연산으로부터 발생할 수 있는 +여러 종류의 에러를 표현하는 variant를 가진, 표준 라이브러리에서 제공하는 열거형입니다. 우리가 +관심을 갖고 있는 variant는 `ErrorKind::NotFound`인데, 이는 열고자 하는 파일이 아직 존재하지 +않음을 나타냅니다. + +조건문 `if error.kind() == ErrorKind::NotFound`는 *매치 가드(match guard)* 라고 +부릅니다: 이는 `match` 줄기 상에서 줄기의 패턴을 좀더 정제해주는 추가 조건문입니다. 그 줄기의 코드가 +실행되기 위해서는 이 조건문이 참이어야 합니다; 그렇지 않다면, 패턴 매칭은 `match`의 다음 줄기에 +맞춰보기 위해 이동할 것입니다. 패턴에는 `ref`가 필요하며 그럼으로써 `error`가 가드 조건문으로 +소유권 이동이 되지 않고 그저 참조만 됩니다. 패턴 내에서 참조자를 얻기 위해 `&`대신 `ref`이 사용되는 +이유는 18장에서 자세히 다룰 것입니다. 짧게 설명하면, `&`는 참조자를 매치하고 그 값을 제공하지만, +`ref`는 값을 매치하여 그 참조자를 제공합니다. + +매치 가드 내에서 확인하고자 하는 조건문은 `error.kind()`에 의해 반환된 값이 `ErrorKind` 열거형의 +`NotFound` variant인가 하는 것입니다. 만일 그렇다면, `File::create`로 파일 생성을 시도합니다. +그러나, `File::create` 또한 실패할 수 있기 때문에, 안쪽에 `match` 구문을 바깥쪽과 마찬가지로 추가할 +필요가 있습니다! 파일이 열수 없을 때, 다른 에러 메세지가 출력될 것입니다. 바깥쪽 `match`의 마지막 갈래는 +똑같이 남아서, 파일을 못 찾는 에러 외에 다른 어떤 에러에 대해서도 패닉을 일으킵니다. + +### 에러가 났을 때 패닉을 위한 숏컷: `unwrap`과 `expect` + +`match`의 사용은 충분히 잘 동작하지만, 살짝 장황하기도 하고 의도를 항상 잘 전달하는 것도 아닙니다. +`Result` 타입은 다양한 것을 하기 위해 정의된 수많은 헬퍼 메소드를 가지고 있습니다. 그 중 하나인 +`unwrap` 이라 부르는 메소드는 Listing 9-3에서 작성한 `match` 구문과 비슷한구현을 한 숏컷 +메소드입니다. 만일 `Result` 값이 `Ok` variant라면, `unwrap`은 `Ok` 내의 값을 반환할 것입니다. +만일 `Result`가 `Err` variant라면, `unwrap`은 우리를 위해 `panic!` 매크로를 호출할 것입니다. ```rust,should_panic use std::fs::File; @@ -221,8 +199,8 @@ fn main() { } ``` -If we run this code without a *hello.txt* file, we’ll see an error message from -the `panic!` call that the `unwrap` method makes: +*hello.txt* 파일이 없는 상태에서 이 코드를 실행시키면, `unwrap` 메소드에 의한 `panic!` +호출로부터의 에러 메세지를 보게 될 것입니다: ```text thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { @@ -230,10 +208,10 @@ repr: Os { code: 2, message: "No such file or directory" } }', /stable-dist-rustc/build/src/libcore/result.rs:868 ``` -There’s another method similar to `unwrap` that lets us also choose the -`panic!` error message: `expect`. Using `expect` instead of `unwrap` and -providing good error messages can convey your intent and make tracking down the -source of a panic easier. The syntax of `expect` looks like this: +또한 `unwrap`과 유사하면서 우리가 `panic!` 에러 메세지를 선택할 수 있게 해주는 또다른 메소드인 +`expect`도 있습니다. `unwrap`대신 `expect`를 이용하고 좋은 에러 메세지를 제공하는 것은 여러분의 +의도를 전달해주고 패닉의 근원을 추적하는 걸 쉽게 해 줄수 있습니다. `expect`의 문법은 아래와 같이 +생겼습니다: ```rust,should_panic use std::fs::File; @@ -243,10 +221,9 @@ fn main() { } ``` -We use `expect` in the same way as `unwrap`: to return the file handle or call -the `panic!` macro. The error message that `expect` uses in its call to -`panic!` will be the parameter that we pass to `expect` instead of the default -`panic!` message that `unwrap` uses. Here’s what it looks like: +`expect`는 `unwrap`과 같은 식으로 사용됩니다: 파일 핸들을 리턴하거나 `panic!` 매크로를 호출하는 +것이죠. `expect`가 `panic!` 호출에 사용하는 에러 메세지는 `unwrap`이 사용하는 기본 `panic!` +메세지 대신 `expect`에 넘기는 파라미터로 설정될 것입니다. 아래에 어떻게 생겼는지에 대한 예가 있습니다: ```text thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code: @@ -254,18 +231,15 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code: /stable-dist-rustc/build/src/libcore/result.rs:868 ``` -### Propagating Errors +### 에러 전파하기 -When writing a function whose implementation calls something that might fail, -instead of handling the error within this function, you can choose to let your -caller know about the error so they can decide what to do. This is known as -*propagating* the error, and gives more control to the calling code where there -might be more information or logic that dictates how the error should be -handled than what you have available in the context of your code. +실패할지도 모르는 무언가를 호출하는 구현을 가진 함수를 작성할때, 이 함수 내에서 에러를 처리하는 대신, +호출자가 에러를 알게 하여 그쪽에서 어떻게 할지 결정하도록 할 수 있습니다. 이는 에러 *전파하기*로 알려져 +있으며, 에러가 어떻게 처리해야 좋을지 좌우해야 할 상황에서, 여러분의 코드 내용 내에서 이용 가능한 것들보다 +더 많은 정보와 로직을 가지고 있을 수도 있는 호출하는 코드쪽에 더 많은 제어권을 줍니다. -For example, Listing 9-5 shows a function that reads a username from a file. If -the file doesn’t exist or can’t be read, this function will return those errors -to the code that called this function: +예를 들면, Listing 9-5는 파일로부터 사용자 이름을 읽는 함수를 작성한 것입니다. 만일 파일이 존재하지 +않거나 읽을 수 없다면, 이 함수는 호출하는 코드쪽으로 해당 에러를 반환할 것입니다: ```rust use std::io; @@ -289,58 +263,45 @@ fn read_username_from_file() -> Result { } ``` -Listing 9-5: A function that returns errors to the -calling code using `match` - -Let’s look at the return type of the function first: `Result`. This means that the function is returning a value of the type -`Result` where the generic parameter `T` has been filled in with the -concrete type `String`, and the generic type `E` has been filled in with the -concrete type `io::Error`. If this function succeeds without any problems, the -caller of this function will receive an `Ok` value that holds a `String` — the -username that this function read from the file. If this function encounters any -problems, the caller of this function will receive an `Err` value that holds an -instance of `io::Error` that contains more information about what the problems -were. We chose `io::Error` as the return type of this function because that -happens to be the type of the error value returned from both of the operations -we’re calling in this function’s body that might fail: the `File::open` -function and the `read_to_string` method. - -The body of the function starts by calling the `File::open` function. Then we -handle the `Result` value returned with a `match` similar to the `match` in -Listing 9-3, only instead of calling `panic!` in the `Err` case, we return -early from this function and pass the error value from `File::open` back to the -caller as this function’s error value. If `File::open` succeeds, we store the -file handle in the variable `f` and continue. - -Then we create a new `String` in variable `s` and call the `read_to_string` -method on the file handle in `f` in order to read the contents of the file into -`s`. The `read_to_string` method also returns a `Result` because it might fail, -even though `File::open` succeeded. So we need another `match` to handle that -`Result`: if `read_to_string` succeeds, then our function has succeeded, and we -return the username from the file that’s now in `s` wrapped in an `Ok`. If -`read_to_string` fails, we return the error value in the same way that we -returned the error value in the `match` that handled the return value of -`File::open`. We don’t need to explicitly say `return`, however, since this is -the last expression in the function. - -The code that calls this code will then handle getting either an `Ok` value -that contains a username or an `Err` value that contains an `io::Error`. We -don’t know what the caller will do with those values. If they get an `Err` -value, they could choose to call `panic!` and crash their program, use a -default username, or look up the username from somewhere other than a file, for -example. We don’t have enough information on what the caller is actually trying -to do, so we propagate all the success or error information upwards for them to -handle as they see fit. - -This pattern of propagating errors is so common in Rust that there is dedicated -syntax to make this easier: `?`. - -### A Shortcut for Propagating Errors: `?` - -Listing 9-6 shows an implementation of `read_username_from_file` that has the -same functionality as it had in Listing 9-5, but this implementation uses the -question mark operator: +Listing 9-5: `match`를 이용하여 호출 코드 쪽으로 에러를 반환하는 함수 + + +함수의 반환 타입부터 먼저 살펴봅시다: `Result`. 이는 함수가 `Result` +타입의 값을 반환하는데 제네릭 파라미터 `T`는 구체적 타입(concrete type)인 `String`로 채워져 있고, +제네릭 타입 `E`는 구체적 타입인 `io::Error`로 채워져 있습니다. 만일 이 함수가 어떤 문제 없이 성공하면, +함수의 호출자는 `String`을 담은 값을 받을 것입니다 - 이 함수가 파일로부터 읽어들인 사용자 이름이겠지요. +만일 어떤 문제가 발생한다면, 함수의 호출자는 문제가 무엇이었는지에 대한 더 많은 정보를 담고 있는 +`io::Error`의 인스턴스를 담은 `Err` 값을 받을 것입니다. 이 함수의 반환 타입으로서 `io::Error`를 +선택했는데, 그 이유는 우리가 이 함수 내부에서 호출하고 있는 실패 가능한 연산 두 가지가 모두 이 타입의 +에러 값을 반환하기 때문입니다: `File::open` 함수와 `read_to_string` 메소드 말이죠. + +함수의 본체는 `File::open` 함수를 호출하면서 시작합니다. 그 다음에는 Listing 9-3에서 본 `match`와 +유사한 식으로 `match`을 이용해서 `Result` 값을 처리하는데, `Err` 경우에 `panic!`을 호출하는 대신 +이 함수를 일찍 끝내고 `File::open`으로부터의 에러 값을 마치 이 함수의 에러 값인것처럼 호출자에게 +전달합니다. 만일 `File::open`이 성공하면, 파일 핸들을 `f`에 저장하고 계속합니다. + +그 뒤 변수 `s`에 새로운 `String`을 생성하고 파일의 콘텐츠를 읽어 `s`에 넣기 위해 `f`에 있는 +파일 핸들의 `read_to_string` 메소드를 호출합니다. `File::open`가 성공하더라도 `read_to_string` +메소드가 실패할 수 있기 때문에 이 함수 또한 `Result`를 반환합니다. 따라서 이 `Result`를 처리하기 +위해서 또다른 `match`가 필요합니다: 만일 `read_to_string`이 성공하면, 우리의 함수가 성공한 +것이고, 이제 `s` 안에 있는 파일로부터 읽어들인 사용자 이름을 `Ok`에 싸서 반환합니다. 만일 +`read_to_string`이 실패하면, `File::open`의 반환값을 처리했던 `match`에서 에러값을 +반환하는 것과 같은 방식으로 에러 값을 반환합니다. 하지만 여기서는 명시적으로 `return`이라 말할 필요는 +없는데, 그 이유는 이 함수의 마지막 표현식이기 때문입니다. + +그러면 이 코드를 호출하는 코드는 사용자 이름을 담은 `Ok` 값 혹은 `io::Error`를 담은 `Err` 값을 +얻는 처리를 하게 될 것입니다. 호출자가 이 값을을 가지고 어떤 일을 할 것인지 우리는 알지 못합니다. +만일 그들이 `Err` 값을 얻었다면, 예를 들면 `panic!`을 호출하여 프로그램을 종료시키는 선택을 할 +수도 있고, 기본 사용자 이름을 사용할 수도 있으며, 혹은 파일이 아닌 다른 어딘가에서 사용자 이름을 +찾을 수도 있습니다. 호출자가 정확히 어떤 것을 시도하려 하는지에 대한 충분한 정보가 없기 때문에, +우리는 모든 성공 혹은 에러 정보를 위로 전파하여 호출자가 그들에 맞는 처리를 하도록 합니다. + +러스트에서 에러를 전파하는 패턴은 너무 흔하여 이를 더 쉽게 해주는 전용 문법이 있습니다: 바로 `?` 입니다. + +### 에러를 전파하기 위한 숏컷: `?` + +Listing 9-6은 Listing 9-5과 같은 기능을 가진 `read_username_from_file`의 구현을 보여주는데, +다만 이 구현은 물음표 연산자를 이용하고 있습니다: ```rust use std::io; @@ -355,26 +316,19 @@ fn read_username_from_file() -> Result { } ``` -Listing 9-6: A function that returns errors to the -calling code using `?` +Listing 9-6: `?`를 이용하여 에러를 호출 코드쪽으로 반환하는 함수 -The `?` placed after a `Result` value is defined to work the exact same way as -the `match` expressions we defined to handle the `Result` values in Listing -9-5. If the value of the `Result` is an `Ok`, the value inside the `Ok` will -get returned from this expression and the program will continue. If the value -is an `Err`, the value inside the `Err` will be returned from the whole -function as if we had used the `return` keyword so that the error value gets -propagated to the caller. +`Result` 값 뒤의 `?`는 Listing 9-5에서 `Result` 값을 다루기 위해 정의했던 `match` 표현식과 +정확히 같은 방식으로 동작하게끔 정의되어 있습니다. 만일 `Result`의 값이 `Ok`라면, `Ok` 내의 값이 +이 표현식으로부터 얻어지고 프로그램이 계속됩니다. 만일 값이 `Err`라면, 우리가 `return` 키워드를 +사용하여 에러 값을 호출자에게 전파하는 것과 같이 전체 함수로부터 `Err` 내의 값이 반환될 것입니다. -In the context of Listing 9-6, the `?` at the end of the `File::open` call will -return the value inside an `Ok` to the variable `f`. If an error occurs, `?` -will return early out of the whole function and give any `Err` value to our -caller. The same thing applies to the `?` at the end of the `read_to_string` -call. +Listing 9-6의 내용에서, `File::open` 호출 부분의 끝에 있는 `?`는 `Ok`내의 값을 변수 `f`에게 +반환해줄 것입니다. 만일 에러가 발생하면 `?`는 전체 함수로부터 일찍 빠져나와 호출자에게 어떤 `Err` 값을 +줄 것입니다. `read_to_string` 호출의 끝부분에 있는 `?`도 같은 것이 적용되어 있습니다. -The `?` eliminates a lot of boilerplate and makes this function’s -implementation simpler. We could even shorten this code further by chaining -method calls immediately after the `?`: +`?`는 많은 수의 보일러플레이트(boilerplate)를 제거해주고 이 함수의 구현을 더 단순하게 만들어 줍니다. +심지어는 `?` 뒤에 바로 메소드 호출을 연결하는 식으로 이 코드를 더 줄일 수도 있습니다: ```rust use std::io; @@ -390,25 +344,22 @@ fn read_username_from_file() -> Result { } ``` -We’ve moved the creation of the new `String` in `s` to the beginning of the -function; that part hasn’t changed. Instead of creating a variable `f`, we’ve -chained the call to `read_to_string` directly onto the result of -`File::open("hello.txt")?`. We still have a `?` at the end of the -`read_to_string` call, and we still return an `Ok` value containing the -username in `s` when both `File::open` and `read_to_string` succeed rather than -returning errors. The functionality is again the same as in Listing 9-5 and -Listing 9-6, this is just a different, more ergonomic way to write it. +새로운 `String`을 만들어 `s`에 넣는 부분을 함수의 시작 부분으로 옮겼습니다; 이 부분은 달라진 것이 +없습니다. `f` 변수를 만드는 대신, `File::open("hello.txt")?`의 결과 바로 뒤에 +`read_to_string`의 호출을 연결시켰습니다. `read_to_string` 호출의 끝에는 여전히 `?`가 +남아있고, `File::open`과 `read_to_string`이 모두 에러를 반환하지 않고 성공할 때 +`s` 안의 사용자 이름을 담은 `Ok`를 여전히 반환합니다. 함수의 기능 또한 Lsting 9-5와 +Listing 9-6의 것과 동일하고, 다만 작성하기에 더 인체공학적인 방법이라는 차이만 있을 뿐입니다. -### `?` Can Only Be Used in Functions That Return `Result` +### `?`는 `Result`를 반환하는 함수에서만 사용될 수 있습니다 -The `?` can only be used in functions that have a return type of `Result`, -since it is defined to work in exactly the same way as the `match` expression -we defined in Listing 9-5. The part of the `match` that requires a return type -of `Result` is `return Err(e)`, so the return type of the function must be a -`Result` to be compatible with this `return`. +`?`는 `Result` 타입을 반환하는 함수에서만 사용이 가능한데, 이것이 Listing 9-5에 정의된 `match` +표현식과 정확히 동일한 방식으로 동작하도록 정의되어 있기 때문입니다. `Result` 반환 타입을 요구하는 +`match` 부분은 `return Err(e)`이며, 따라서 함수의 반환 타입은 반드시 이 `return`과 호환 가능한 +`Result`가 되어야 합니다. -Let’s look at what happens if we use `?` in the `main` function, which you’ll -recall has a return type of `()`: +`main`의 반환 타입이 `()`라는 것을 상기하면서, 만약 `main` 함수 내에서 `?`를 사용하면 어떤일이 생길지 +살펴봅시다: ```rust,ignore use std::fs::File; @@ -427,7 +378,7 @@ https://github.com/rust-lang/rust/issues/35946 fixed soon, hopefully before this chapter gets through copy editing-- at that point I'll make sure to update this error message. /Carol --> -When we compile this, we get the following error message: +이걸 컴파일하면, 아래와 같은 에러 메세지가 뜹니다: ```text error[E0308]: mismatched types @@ -441,12 +392,11 @@ error[E0308]: mismatched types = note: found type `std::result::Result<_, _>` ``` -This error is pointing out that we have mismatched types: the `main` function -has a return type of `()`, but the `?` might return a `Result`. In functions -that don’t return `Result`, when you call other functions that return `Result`, -you’ll need to use a `match` or one of the `Result` methods to handle it, -instead of using `?` to potentially propagate the error to the caller. +이 에러는 잘못 매칭된 타입이 있음을 지적합니다: `main` 함수의 반환 타입은 `()`인데, `?`는 +`Result`를 반환할지도 모른다는 것이죠. `Result`를 반환하지 않는 함수 내에서 여러분이 `Result`를 +반환하는 다른 함수를 호출한다면, 여러분은 이를 처리하는데 있어 `?`를 이용하여 호출자에게 가능한 에러를 +전파하는 대신, `match` 혹은 `Result` 메소드 중 하나를 이용할 필요가 있을 것입니다. +(역주: 현재는 좀 더 친절하게 상황을 설명하는 에러 메세지가 나오지만, 맥락은 유사합니다.) -Now that we’ve discussed the details of calling `panic!` or returning `Result`, -let’s return to the topic of how to decide which is appropriate to use in which -cases. +`panic!`을 호출하거나 `Result`를 반환하는 것의 자세한 부분을 논의했으니, 어떤 경우에 어떤 방법을 +사용하는 것이 적합할지를 어떻게 결정하는가에 대한 주제로 돌아갑시다. diff --git a/second-edition/src/ch09-03-to-panic-or-not-to-panic.md b/second-edition/src/ch09-03-to-panic-or-not-to-panic.md index c2acfdbe39..aa41e33e28 100644 --- a/second-edition/src/ch09-03-to-panic-or-not-to-panic.md +++ b/second-edition/src/ch09-03-to-panic-or-not-to-panic.md @@ -1,52 +1,44 @@ -## To `panic!` or Not To `panic!` - -So how do you decide when you should `panic!` and when you should return -`Result`? When code panics, there’s no way to recover. You could choose to call -`panic!` for any error situation, whether there’s a possible way to recover or -not, but then you’re making the decision for your callers that a situation is -unrecoverable. When you choose to return a `Result` value, you give your caller -options, rather than making the decision for them. They could choose to attempt -to recover in a way that’s appropriate for their situation, or they could -decide that actually, an `Err` value in this case is unrecoverable, so they can -call `panic!` and turn your recoverable error into an unrecoverable one. -Therefore, returning `Result` is a good default choice when you’re defining a -function that might fail. - -There are a few situations in which it’s more appropriate to write code that -panics instead of returning a `Result`, but they are less common. Let’s discuss -why it’s appropriate to panic in examples, prototype code, and tests, then -situations where you as a human can know a method won’t fail that the compiler -can’t reason about, and conclude with some general guidelines on how to decide -whether to panic in library code. - -### Examples, Prototype Code, and Tests: Perfectly Fine to Panic - -When you’re writing an example to illustrate some concept, having robust error -handling code in the example as well can make the example less clear. In -examples, it’s understood that a call to a method like `unwrap` that could -`panic!` is meant as a placeholder for the way that you’d actually like your -application to handle errors, which can differ based on what the rest of your -code is doing. - -Similarly, the `unwrap` and `expect` methods are very handy when prototyping, -before you’re ready to decide how to handle errors. They leave clear markers in -your code for when you’re ready to make your program more robust. - -If a method call fails in a test, we’d want the whole test to fail, even if -that method isn’t the functionality under test. Because `panic!` is how a test -gets marked as a failure, calling `unwrap` or `expect` is exactly what makes -sense to do. - -### Cases When You Have More Information Than The Compiler - -It would also be appropriate to call `unwrap` when you have some other logic -that ensures the `Result` will have an `Ok` value, but the logic isn’t -something the compiler understands. You’ll still have a `Result` value that you -need to handle: whatever operation you’re calling still has the possibility of -failing in general, even though it’s logically impossible in your particular -situation. If you can ensure by manually inspecting the code that you’ll never -have an `Err` variant, it is perfectly acceptable to call `unwrap`. Here’s an -example: +## `panic!`이냐, `panic!`이 아니냐, 그것이 문제로다 + +그러면 언제 `panic!`을 써야하고 언제 `Result`를 반환할지 어떻게 결정해야 할까요? 코드가 패닉을 +일으킬때는 복구할 방법이 없습니다. 복구 가능한 방법이 있든 혹은 그렇지 않든 여러분은 어떤 에러 상황에 +대해 `panic!`을 호출하는 것을 택할수 있지만, 그렇다면 여러분은 여러분의 코드를 호출하는 사람에게 +현 상황은 복구 불가능한 것이라고 결정을 내리는 겁니다. 여러분이 `Result` 값을 반환하는 선택을 한다면, +호출자에게 결단을 내려주기 보다는 옵션을 제공하는 것입니다. 그들은 그들의 상황에 적합한 방식으로 +복구를 시도할 수도 있고, 혹은 현재 상황의 `Err`은 복구 불가능하다고 사실상 결론을 내려서 +`panic!`을 호출하여 여러분이 만든 복구 가능한 에러를 복구 불가능한 것으로 바꿔놓을 수도 있습니다. +그러므로, 여러분이 실패할지도 모르는 함수를 정의할 때는 `Result`을 반환하는 것이 기본적으로 좋은 +선택입니다. + +`Result`를 반환하는 대신 패닉을 일으키는 코드를 작성하는 것이 더 적합한 몇 가지 상황이 있지만, +덜 일반적입니다. 예제, 프로토타입 코드 및 테스트의 경우, 그리고 실패할 리 없는 메소드라는 것을 +사람으로서의 여러분이라면 알수 있지만 컴파일러는 이유를 파악할 수 없는 경우들을 보면서 패닉을 +일으키는 것이 왜 적합한지를 논의해보고, 라이브러리 코드에 패닉을 추가해야 할지를 어떻게 결정할까에 대한 +일반적인 가이드라인을 내림으로서 결론지어 봅시다. + +### 예제, 프로토타입 코드, 그리고 테스트: 패닉을 일으켜도 완전 괜찮습니다 + +여러분이 어떤 개념을 그려내기 위한 예제를 작성중이라면, 강건한 에러 처리 코드를 예제 안에 넣는 것은 +또한 예제를 덜 깨끗하게 만들 수 있습니다. 예제 코드 내에서는 `panic!`을 일으킬 수 있는 `unwrap` +같은 메소드를 호출하는 것이 여러분의 어플리케이션이 에러를 처리하고자 하는 방법에 대한 플레이스홀더로서의 +의미를 갖는데, 이는 여러분의 코드의 나머지 부분이 어떤 것을 하는지에 따라 달라질 수 있습니다. + +비슷한 상황에서, 여러분이 에러를 어떻게 처리할지 결정할 준비가 되기 전에는, `unwrap`과 `expect` +메소드가 프로토타이핑을 할때 매우 편리합니다. 이 함수들은 여러분의 코드를 더 강건하게 만들 준비가 되었을 +때를 위해서 명확한 표시를 남겨 둡니다. + +만일 테스트 내에서 메소드 호출이 실패한다면, 해당 메소드가 테스트 중인 기능이 아니더라도 전체 테스트를 +실패시키도록 합니다. `panic!`은 테스트가 어떻게 실패하는지 표시해주기 때문에, `unwrap`이나 +`expect`를 호출하는 것은 정확하게 하고자 하는 일과 일치합니다. + +### 컴파일러보다 어려분이 더 많은 정보를 가지고 있을 때 + +`Result`가 `Ok` 값을 가지고 있을 거라 확신할 다른 논리를 가지고 있지만, 그 논리가 컴파일러에 의해 +이해할 수 있는 것이 아닐 때라면, `unwrap`을 호출하는 것이 또한 적절할 수 있습니다. 여러분은 여전히 +처리할 필요가 있는 `Result` 값을 가지고 있습니다: 여러분의 특정한 상황에서 논리적으로 불가능할지라도, +여러분이 호출하고 있는 연산이 무엇이든간에 일반적으로는 여전히 실패할 가능성이 있습니다. 만일 여러분이 +수동적으로 `Err` variant를 결코 발생시키지 않는 코드를 조사하여 확신할 수 있다면, `unwrap`을 +호출하는 것이 완벽히 허용됩니다. 여기 예제가 있습니다: ```rust use std::net::IpAddr; @@ -54,88 +46,72 @@ use std::net::IpAddr; let home = "127.0.0.1".parse::().unwrap(); ``` -We’re creating an `IpAddr` instance by parsing a hardcoded string. We can see -that `127.0.0.1` is a valid IP address, so it’s acceptable to use `unwrap` -here. However, having a hardcoded, valid string doesn’t change the return type -of the `parse` method: we still get a `Result` value, and the compiler will -still make us handle the `Result` as if the `Err` variant is still a -possibility since the compiler isn’t smart enough to see that this string is -always a valid IP address. If the IP address string came from a user instead of -being hardcoded into the program, and therefore *did* have a possibility of -failure, we’d definitely want to handle the `Result` in a more robust way -instead. - -### Guidelines for Error Handling - -It’s advisable to have your code `panic!` when it’s possible that you could end -up in a bad state—in this context, bad state is when some assumption, -guarantee, contract, or invariant has been broken, such as when invalid values, -contradictory values, or missing values are passed to your code—plus one or -more of the following: - -* The bad state is not something that’s *expected* to happen occasionally -* Your code after this point needs to rely on not being in this bad state -* There’s not a good way to encode this information in the types you use - -If someone calls your code and passes in values that don’t make sense, the best -thing might be to `panic!` and alert the person using your library to the bug -in their code so that they can fix it during development. Similarly, `panic!` -is often appropriate if you’re calling external code that is out of your -control, and it returns an invalid state that you have no way of fixing. - -When a bad state is reached, but it’s expected to happen no matter how well you -write your code, it’s still more appropriate to return a `Result` rather than -calling `panic!`. Examples of this include a parser being given malformed data, -or an HTTP request returning a status that indicates you have hit a rate limit. -In these cases, you should indicate that failure is an expected possibility by -returning a `Result` in order to propagate these bad states upwards so that the -caller can decide how they would like to handle the problem. To `panic!` -wouldn’t be the best way to handle these cases. - -When your code performs operations on values, your code should verify the -values are valid first, and `panic!` if the values aren’t valid. This is mostly -for safety reasons: attempting to operate on invalid data can expose your code -to vulnerabilities. This is the main reason that the standard library will -`panic!` if you attempt an out-of-bounds array access: trying to access memory -that doesn’t belong to the current data structure is a common security problem. -Functions often have *contracts*: their behavior is only guaranteed if the -inputs meet particular requirements. Panicking when the contract is violated -makes sense because a contract violation always indicates a caller-side bug, -and it is not a kind of error you want callers to have to explicitly handle. In -fact, there’s no reasonable way for calling code to recover: the calling -*programmers* need to fix the code. Contracts for a function, especially when a -violation will cause a panic, should be explained in the API documentation for -the function. - -Having lots of error checks in all of your functions would be verbose and -annoying, though. Luckily, you can use Rust’s type system (and thus the type -checking the compiler does) to do a lot of the checks for you. If your function -has a particular type as a parameter, you can proceed with your code’s logic -knowing that the compiler has already ensured you have a valid value. For -example, if you have a type rather than an `Option`, your program expects to -have *something* rather than *nothing*. Your code then doesn’t have to handle -two cases for the `Some` and `None` variants, it will only have one case for -definitely having a value. Code trying to pass nothing to your function won’t -even compile, so your function doesn’t have to check for that case at runtime. -Another example is using an unsigned integer type like `u32`, which ensures the -parameter is never negative. - -### Creating Custom Types for Validation - -Let’s take the idea of using Rust’s type system to ensure we have a valid value -one step further, and look at creating a custom type for validation. Recall the -guessing game in Chapter 2, where our code asked the user to guess a number -between 1 and 100. We actually never validated that the user’s guess was -between those numbers before checking it against our secret number, only that -it was positive. In this case, the consequences were not very dire: our output -of “Too high” or “Too low” would still be correct. It would be a useful -enhancement to guide the user towards valid guesses, though, and have different -behavior when a user guesses a number that’s out of range versus when a user -types, for example, letters instead. - -One way to do this would be to parse the guess as an `i32` instead of only a -`u32`, to allow potentially negative numbers, then add a check for the number -being in range: +여기서는 하드코딩된 스트링을 파싱하여 `IpAddr` 인스턴스를 만드는 중입니다. 우리는 `127.0.0.1`이 +유효한 IP 주소임을 볼 수 있으므로, 여기서 `unwrap`을 사용하는 것은 허용됩니다. 그러나, 하드코딩된 +유효한 스트링을 갖고 있다는 것이 `parse` 메소드의 반환 타입을 변경해주지는 않습니다: 우리는 여전히 +`Result` 값을 갖게되고, 컴파일러는 마치 `Err` variant가 나올 가능성이 여전히 있는 것처럼 +우리가 `Result`를 처리하도록 할 것인데, 그 이유는 이 스트링이 항상 유효한 IP 주소라는 것을 +알 수 있을만큼 컴파일러가 똑똑하지는 않기 때문입니다. 만일 IP 주소 스트링이 프로그램 내에 하드코딩된 +것 대신 사용자로부터 입력되었다면, 그래서 실패할 가능성이 *생겼다면*, 우리는 대신 더 강건한 방식으로 +`Result`를 처리할 필요가 분명히 있습니다. + +### 에러 처리를 위한 가이드라인 + +여러분이 결국 나쁜 상태에 처하게 될 가능성이 있을 때는 여러분의 코드에 `panic!`을 넣는 것이 +바람직한데 - 이 글에서 말하는 나쁜 상태란 어떤 가정, 보장, 계약, 혹은 불변성이 깨질 때를 뜻하는 +것으로, 이를테면 유효하지 않은 값이나 모순되는 값, 혹은 찾을 수 없는 값이 여러분의 코드를 통과할 +경우를 말합니다 - 아래에 쓰여진 상황중 하나 혹은 그 이상일 경우라면 말이죠: + +* 이 나쁜 상태란 것이 가끔 벌어질 것으로 *예상되는* 무언가가 아닙니다 +* 그 시점 이후의 코드는 이 나쁜 상태에 있지 않아야만 할 필요가 있습니다 +* 여러분이 사용하고 있는 타입 내에 이 정보를 집어 넣을만한 뾰족한 수가 없습니다 + +만일 어떤 사람이 여러분의 코드를 호출하고 타당하지 않은 값을 집어넣었다면, `panic!`을 써서 여러분의 +라이브러리를 사용하고 있는 사람에게 그들의 코드 내의 버그를 알려서 개발하는 동안 이를 고칠 수 있게끔 +하는 것이 최선책일 수도 있습니다. 비슷한 식으로, 만일 여러분의 제어권을 벗어난 외부 코드를 호출하고 +있고, 이것이 여러분이 고칠 방법이 없는 유효하지 않은 상태를 반환한다면, `panic!`이 종종 적합합니다. + +나쁜 상태에 도달했지만, 여러분이 얼마나 코드를 잘 작성했던 간에 일어날 것으로 예상될 때라면 `panic!`을 +호출하는 것보다 `Result`를 반환하는 것이 여전히 더 적합합니다. 이에 대한 예는 기형적인 데이터가 주어지는 +파서나, 속도 제한에 달했음을 나타내는 상태를 반환하는 HTTP 요청 등을 포함합니다. 이러한 경우, 여러분은 +이러한 나쁜 상태를 위로 전파하여 위해 호출자가 그 문제를 어떻게 처리할지를 결정할 수 있도록 하기 위해서 +`Result`를 반환하는 방식으로 실패가 예상 가능한 것임을 알려줘야 합니다. `panic!`에 빠지는 것은 +이러한 경우를 처리하는 최선의 방식이 아닐 것입니다. + +여러분의 코드가 어떤 값에 대해 연산을 수행할 때, 여러분의 코드는 해당 값이 유효한지를 먼저 검사하고, +만일 그렇지 않다면 `panic!`을 호출해야 합니다. 이는 주로 안전상의 이유를 위한 것입니다: 유효하지 +않은 데이터 상에서 어떤 연산을 시도하는 것은 여러분의 코드를 취약점에 노출시킬 수 있습니다. 이는 +여러분이 범위를 벗어난 배열 접근을 시도했을 경우 표준 라이브러리가 `panic!`을 호출하는 주된 이유입니다: +현제의 데이터 구조가 소유하지 않은 메모리를 접근 시도하는 것은 흔한 보안 문제입니다. 함수는 종종 +*계약*을 갖고 있습니다: 입력이 특정 요구사항을 만족시킬 경우에만 함수의 행동이 보장됩니다. 이 계약을 +위반했을 때 패닉에 빠지는 것은 사리에 맞는데, 그 이유는 계약 위반이 언제나 호출자 쪽의 버그임을 나타내고, +이는 호출자가 명시적으로 처리하도록 하는 종류의 버그가 아니기 때문입니다. 사실, 호출하는 쪽의 코드가 +복구시킬 합리적인 방법은 없습니다: 호출하는 *프로그래머*는 그 코드를 고칠 필요가 있습니다. 함수에 대한 +계약은, 특히 계약 위반이 패닉의 원인이 될 때는, 그 함수에 대한 API 문서에 설명되어야 합니다. + +하지만 여러분의 모든 함수들 내에서 수많은 에러 검사를 한다는 것은 장황하고 짜증날 것입니다. 다행스럽게도, +러스트의 타입 시스템이 (그리고 컴파일러가 하는 타입 검사 기능이) 이러분을 위해 수많은 검사를 해줄 수 +있습니다. 여러분의 함수가 특정한 타입을 파라미터로 갖고 있다면, 여러분이 유효한 값을 갖는다는 것을 +컴파일러가 이미 보장했음을 아는 상태로 여러분의 코드 로직을 진행할 수 있습니다. 예를 들면, 만약 +여러분이 `Option`이 아닌 어떤 타입을 갖고 있다면, 여러분의 프로그램은 *아무것도 아닌 것*이 아닌 +*무언가*를 갖고 있음을 예측합니다. 그러면 여러분의 코드는 `Some`과 `None` variant에 대한 두 +경우를 처리하지 않아도 되며, 분명히 값을 가지고 있는 하나의 경우만 있을 것입니다. 여러분의 함수에 +아무것도 넘기지 않는 시도를 하는 코드는 컴파일 조차 되지 않을 것이고, 따라서 여러분의 함수는 +그러한 경우에 대해서 런타임에 검사하지 않아도 됩니다. 또다른 예로는 `u32`와 같은 부호없는 정수를 +이용하는 것이 있는데, 이는 파라미터가 절대 음수가 아님을 보장합니다. + +### 유효성을 위한 커스텀 타입 생성하기 + +러스트의 타입 시스템을 이용하여 유효한 값을 보장하는 아이디어에서 한발 더 나가서, 유효성을 위한 +커스텀 타입을 생성하는 것을 살펴봅시다. 2장의 추리 게임을 상기해 보시면, 우리의 코드는 사용자에게 +1부터 100사이의 숫자를 추측하도록 요청했었죠. 우리는 실제로는 사용자의 추측값이 우리의 비밀 숫자와 +비교하기 전에 해당 값이 유효한지 결코 확인하지 않았고, 오직 양수임을 확인했습니다. 이 경우, 결과는 +매우 끔찍하지는 않았습니다: “Too high”나 “Too low”라고 표시했던 출력은 여전히 맞을 것입니다. +하지만 사용자에게 유효한 추측값을 안내해주고, 사용자가 예를 들어 글자를 입력했을 때에 비해 사용자가 +범위 밖의 값을 추측했을 때 다른 동작을 하는 것은 쓸모있는 향상일 것입니다. + +이를 위한 한가지 방법은 `u32` 대신 `i32`로서 추측값을 파싱하여 음수가 입력될 가능성을 허용하고, +숫자가 범위 내에 있는지에 대한 검사를 추가하는 것일테지요: ```rust,ignore loop { @@ -156,23 +132,20 @@ loop { } ``` -The `if` expression checks to see if our value is out of range, tells the user -about the problem, and calls `continue` to start the next iteration of the loop -and ask for another guess. After the `if` expression, we can proceed with the -comparisons between `guess` and the secret number knowing that `guess` is -between 1 and 100. +`if` 표현식은 우리의 값이 범위 밖에 있는지 검사하고, 사용자에게 문제점을 말해주고, `continue`를 +호출하여 루프의 다음 반복을 시작하고 다른 추측값을 요청해줍니다. `if` 표현식 이후에는, `guess`가 +1과 100 사이의 값이라는 것을 아는 상태에서 `guess`와 비밀 숫자의 비교를 진행할 수 있습니다. -However, this is not an ideal solution: if it was absolutely critical that the -program only operated on values between 1 and 100, and it had many functions -with this requirement, it would be tedious (and potentially impact performance) -to have a check like this in every function. +하지만, 이는 이상적인 해결책이 아닙니다: 만일 프로그램이 오직 1과 100 사이의 값에서만 동작하는 것이 +전적으로 중요하고, 많은 함수가 이러한 요구사항을 가지고 있다면, 모든 함수 내에서 이렇게 검사를 하는 것은 +지루할 것입니다. (그리고 잠재적으로 성능에 영향을 줄 것입니다.) + +대신, 우리는 새로운 타입을 만들어서, 유효성 확인을 모든 곳에서 반복하는 것보다는 차라리 그 타입의 +인스턴스를 생성하는 함수 내에 유효성 확인을 넣을 수 있습니다. 이 방식에서, 함수가 그 시그니처 내에서 +새로운 타입을 이용하고 받은 값을 자신있게 사용하는 것은 안전합니다. Listing 9-8은 `new` 함수가 +1과 100 사이의 값을 받았을 때에만 인스턴스를 생성하는 `Guess` 타입을 정의하는 한가지 방법을 +보여줍니다: -Instead, we can make a new type and put the validations in a function to create -an instance of the type rather than repeating the validations everywhere. That -way, it’s safe for functions to use the new type in their signatures and -confidently use the values they receive. Listing 9-8 shows one way to define a -`Guess` type that will only create an instance of `Guess` if the `new` function -receives a value between 1 and 100: ```rust pub struct Guess { @@ -196,51 +169,44 @@ impl Guess { } ``` -Listing 9-8: A `Guess` type that will only continue with -values between 1 and 100 - -First, we define a struct named `Guess` that has a field named `value` that -holds a `u32`. This is where the number will be stored. - -Then we implement an associated function named `new` on `Guess` that creates -instances of `Guess` values. The `new` function is defined to have one -parameter named `value` of type `u32` and to return a `Guess`. The code in the -body of the `new` function tests `value` to make sure it is between 1 and 100. -If `value` doesn’t pass this test, we call `panic!`, which will alert the -programmer who is calling this code that they have a bug they need to fix, -since creating a `Guess` with a `value` outside this range would violate the -contract that `Guess::new` is relying on. The conditions in which `Guess::new` -might panic should be discussed in its public-facing API documentation; we’ll -cover documentation conventions around indicating the possibility of a `panic!` -in the API documentation that you create in Chapter 14. If `value` does pass -the test, we create a new `Guess` with its `value` field set to the `value` -parameter and return the `Guess`. - -Next, we implement a method named `value` that borrows `self`, doesn’t have any -other parameters, and returns a `u32`. This is a kind of method sometimes -called a *getter*, since its purpose is to get some data from its fields and -return it. This public method is necessary because the `value` field of the -`Guess` struct is private. It’s important that the `value` field is private so -that code using the `Guess` struct is not allowed to set `value` directly: -callers outside the module *must* use the `Guess::new` function to create an -instance of `Guess`, which ensures there’s no way for a `Guess` to have a -`value` that hasn’t been checked by the conditions in the `Guess::new` function. - -A function that has a parameter or returns only numbers between 1 and 100 could -then declare in its signature that it takes or returns a `Guess` rather than a -`u32`, and wouldn’t need to do any additional checks in its body. - -## Summary - -Rust’s error handling features are designed to help you write more robust code. -The `panic!` macro signals that your program is in a state it can’t handle, and -lets you tell the process to stop instead of trying to proceed with invalid or -incorrect values. The `Result` enum uses Rust’s type system to indicate that -operations might fail in a way that your code could recover from. You can use -`Result` to tell code that calls your code that it needs to handle potential -success or failure as well. Using `panic!` and `Result` in the appropriate -situations will make your code more reliable in the face of inevitable problems. - -Now that we’ve seen useful ways that the standard library uses generics with -the `Option` and `Result` enums, let’s talk about how generics work and how you -can make use of them in your code. +Listing 9-8: 1과 100 사이의 값일 때만 계속되는 `Guess` 타입 + + +먼저 `u32`를 갖는 `value`라는 이름의 항목을 가진 `Guess`라는 이름의 구조체를 선언하였습니다. +이것이 숫자가 저장될 곳입니다. + +그런뒤 `Guess` 값의 인스턴스를 생성하는 `new`라는 이름의 연관 함수를 구현하였습니다. +`new` 함수는 `u32` 타입의 값인 `value`를 파라미터를 갖고 `Guess`를 반환하도록 정의 +되었습니다. `new` 함수의 본체에 있는 코드는 `value`가 1부터 100 사이의 값인지 확인하는 +테스트를 합니다. 만일 `value`가 이 테스트에 통과하지 못하면 `panic!`을 호출하며, +이는 이 코드를 호출하는 프로그래머에게 고쳐야할 버그가 있음을 알려주는데, 범위 밖의 `value`를 +가지고 `Guess`를 생성하는 것은 `Guess::new`가 필요로 하는 계약을 위반하기 때문입니다. +`Guess::new`가 패닉을 일으킬 수도 있는 조건은 공개된 API 문서 내에 다뤄져야 합니다; +여러분이 만드는 API 문서 내에서 `panic!`의 가능성을 가리키는 것에 대한 문서 관례는 14장에서 +다룰 것입니다. 만일 `value`가 테스트를 통과한다면, `value` 항목을 `value` 파라미터로 +설정한 새로운 `Guess`를 생성하여 이 `Guess`를 반환합니다. + +다음으로, `self`를 빌리고, 파라미터를 갖지 않으며, `u32`를 반환하는 `value`라는 이름의 메소드를 +구현했습니다. 이러한 종류 메소드를 종종 *게터(getter)* 라고 부르는데, 그 이유는 이런 함수의 목적이 +객체의 항목으로부터 어떤 데이터를 가져와서 이를 반환하는 것이기 때문입니다. 이 공개 메소드는 `Guess` +구조체의 `value` 항목이 비공개이기 때문에 필요합니다. `value` 항목이 비공개라서 `Guess` 구조체를 +이용하는 코드가 `value`를 직접 설정하지 못하도록 하는 것은 중요합니다: 모듈 밖의 호출자는 *반드시* +`Guess::new` 함수를 이용하여 새로운 `Guess`의 인스턴스를 만들어야 하는데, 이는 `Guess`가 +`Guess::new` 함수의 조건들을 확인한 적이 없는 `value`를 갖는 방법이 없음을 보장합니다. + +그러면 파라미터를 가지고 있거나 오직 1에서 100 사이의 숫자를 반환하는 함수는 `u32` 보다는 `Guess`를 +얻거나 반환하는 시그니처로 선언되고, 더 이상의 확인이 필요치 않을 것입니다. + +## 정리 + +러스트의 에러 처리 기능은 여러분이 더 강건한 코드를 작성하는데 도움을 주도록 설계되었습니다. +`panic!` 매크로는 여러분의 프로그램이 처리 불가능한 상태에 놓어있음에 대한 신호를 주고, 여러분이 +유효하지 않거나 잘못된 값으로 계속 진행하는 시도를 하는 대신 실행을 멈추게끔 해줍니다. `Result` +열거형은 러스트의 타입 시스템을 이용하여 여러분의 코드가 복구할 수 있는 방법으로 연산이 실패할 수도 +있음을 알려줍니다. 또한 `Result`를 이용하면 여러분의 코드를 호출하는 코드에게 잠재적인 성공이나 +실패를 처리해야할 필요가 있음을 알려줄 수 있습니다. `panic!`과 `Result`를 적합한 상황에서 +사용하는 것은 여러분의 코드가 불가피한 문제에 직면했을 때도 더 신뢰할 수 있도록 해줄 것입니다. + +이제 표준 라이브러리가 `Option`과 `Result` 열거형을 가지고 제네릭을 사용하는 유용한 방식들을 +보았으니, 제네릭이 어떤 식으로 동작하고 여러분의 코드에 어떻게 이용할 수 있는지에 대해 이야기해 +보겠습니다.