Move to org mode for posts
This commit is contained in:
parent
3a8dd659dd
commit
02f7e7a1a0
34 changed files with 1056 additions and 893 deletions
|
@ -0,0 +1,84 @@
|
|||
#+TITLE: Handling Multiple Possible Errors in Rust
|
||||
#+DATE: 2022-08-04
|
||||
#+FILETAGS: programming
|
||||
#+HUGO_DRAFT: t
|
||||
|
||||
* Background
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: background
|
||||
:END:
|
||||
Rust handles errors better than any other language I've ever used. All possible errors are known at build time, and by design Rust forces you to at a bare minimum /acknowledge/ that these errors are possible with =.unwrap()=, otherwise writing specific error handling logic.
|
||||
|
||||
** The =Result= enum
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: the-result-enum
|
||||
:END:
|
||||
All of this is thanks to the =Result= enum, which is defined as follows:
|
||||
|
||||
#+begin_src rust
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
}
|
||||
#+end_src
|
||||
|
||||
=Result= is a generic type that requires two type parameters, =T=, the actual value the result is wrapping, and =E=, the error type in case something goes wrong. In the success case, the =Ok(T)= variant will be used, and in an error case, =Err(E)= will be used.
|
||||
|
||||
What makes this powerful is the fact that since =Result= wraps the actual result value, access to that value is prevented unless the possibility of an error is acknowledged.
|
||||
|
||||
** Handling =Result=
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: handling-result
|
||||
:END:
|
||||
For the sake of example, say we're writing a theoretical function, =acquire_banana=, that drives to the store, purchases a banana, and returns the banana.
|
||||
|
||||
We have a function called =drive_to_store=. It returns a =Result= that can either be nothing =()=, or a =NavigationError=.
|
||||
|
||||
#+begin_src rust
|
||||
fn drive_to_store() -> Result<(), NavigationError> { ... }
|
||||
#+end_src
|
||||
|
||||
We then have the following function, =acquire_banana=. It also returns a =Result=, but this time it can either be a =Banana= or a =PurchaseError=.
|
||||
|
||||
#+begin_src rust
|
||||
fn purchase_banana() -> Result<Banana, PurchaseError> { ... }
|
||||
#+end_src
|
||||
|
||||
In other languages, we might write the function something like this:
|
||||
|
||||
#+begin_src rust
|
||||
fn acquire_banana() -> Banana {
|
||||
drive_to_store();
|
||||
purchase_banana()
|
||||
}
|
||||
#+end_src
|
||||
|
||||
However, this doesn't work in Rust. There are two issues here. First, while =drive_to_store();= is in theory valid code (it returns an enum that we ignore), =Result=s *must* be handled. Otherwise, you will get compile warnings. Second, we cannot return =purchase_banana= from the function, because its return type is =Result<Banana, PurchaseError>=, not =Banana=.
|
||||
|
||||
The naïve solution to this would be using the =unwrap= method on the =Result=s, like this:
|
||||
|
||||
#+begin_src rust
|
||||
fn acquire_banana() -> Banana {
|
||||
drive_to_store().unwrap();
|
||||
purchase_banana().unwrap()
|
||||
}
|
||||
#+end_src
|
||||
|
||||
The =.unwrap()= method does exactly what it sounds like. If a =Result= is the =Ok(T)= variant, it returns =T=. However, the naïvety here lies in what happens on the =Err(E)= variant. In that case, it /panics/, printing the error and kills the thread/program. A well-designed program should take errors into account, so this solution is unacceptable.
|
||||
|
||||
The best solution is in this case is *error propagation* using the question mark =?= operator. It works similarly to =.unwrap()=, but instead of panicking on the =Err(E)= case, it returns =Err(E)= from the function. This lets you quickly unwrap results without convoluted =match= statements.
|
||||
|
||||
This lets you convert this:
|
||||
|
||||
#+begin_src rust
|
||||
let x = match get_result() {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
#+end_src
|
||||
|
||||
Into this:
|
||||
|
||||
#+begin_src rust
|
||||
let x = get_result()?;
|
||||
#+end_src
|
Loading…
Add table
Add a link
Reference in a new issue