Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Failing in Rust
Search
Armin Ronacher
April 24, 2018
Programming
5
970
Failing in Rust
A quick talk at a meetup about using failure.
Armin Ronacher
April 24, 2018
Tweet
Share
More Decks by Armin Ronacher
See All by Armin Ronacher
Do Dumb Things
mitsuhiko
0
580
No Assumptions
mitsuhiko
0
170
The Complexity Genie
mitsuhiko
0
200
The Catch in Rye: Seeding Change and Lessons Learned
mitsuhiko
0
320
Runtime Objects in Rust
mitsuhiko
0
350
Rust at Sentry
mitsuhiko
0
460
Overcoming Variable Payloads to Optimize for Performance
mitsuhiko
0
210
Rust API Design Learnings
mitsuhiko
0
560
The Snowball Effect of Open Source
mitsuhiko
0
360
Other Decks in Programming
See All in Programming
UPDATEがシステムを複雑にする? イミュータブルデータモデルのすすめ
shimomura
1
540
CSC307 Lecture 17
javiergs
PRO
0
110
The Evolution of Enterprise Java with Jakarta EE 11 and Beyond
ivargrimstad
1
680
[初登壇@jAZUG]アプリ開発者が気になるGoogleCloud/Azure+wasm/wasi
asaringo
0
130
Claude Codeの使い方
ttnyt8701
1
110
人には人それぞれのサービス層がある
shimabox
3
670
実践ArchUnit ~実例による検証パターンの紹介~
ogiwarat
2
260
SODA - FACT BOOK
sodainc
1
920
FormFlow - Build Stunning Multistep Forms
yceruto
1
170
DroidKnights 2025 - 다양한 스크롤 뷰에서의 영상 재생
gaeun5744
3
180
「ElixirでIoT!!」のこれまでとこれから
takasehideki
0
360
Prism.parseで 300本以上あるエンドポイントに 接続できる権限の一覧表を作ってみた
hatsu38
1
110
Featured
See All Featured
The Cult of Friendly URLs
andyhume
79
6.4k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
123
52k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
16
920
Being A Developer After 40
akosma
90
590k
StorybookのUI Testing Handbookを読んだ
zakiyama
30
5.8k
Practical Orchestrator
shlominoach
188
11k
The Straight Up "How To Draw Better" Workshop
denniskardys
233
140k
RailsConf 2023
tenderlove
30
1.1k
GitHub's CSS Performance
jonrohan
1031
460k
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.8k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
20k
Transcript
Failing in Rust Armin @mitsuhiko Ronacher
None
800°C 36° 2' 0.4662" N 118° 15' 38.7792" W 795°C
789°C 797°C 793°C 805°C 782°C we show you your crashes
— Robert F. Kennedy “Only those who dare to fail
greatly can ever achieve greatly.”
Why do we care?
Errors are Important • Errors are part of your API
• Exceptions let you forget about this easily • A lot more relevant when you can catch them and there are multiple versions of libraries involved
ways to fail greatly
Mechanisms •Result<T, E> •Option<T> •panic!
Result Propagation vs Panic • Results/Options are for handling •
panics are for recovering at best
Examples of Panics • out of bound access • runtime
Examples of Option • safe signalling absence of data •
"the one obvious error"
— Douglas Adams
But when you do •panic!("…"); •unreachable!();
let's talk results
But if you don't panic … how do you result?
fn square_a_number() -> Result<f32, E> { let num = get_a_random_float()?;
Ok(num * num) }
let val = expr?;
let val = match Try::into_result(expr) { Ok(v) => v, Err(e)
=> return Try::from_error(From::from(e)); };
error propagation can be hooked!
The Err in Result can be anything :-/
So let's use some traits for Err
pub trait Error: Debug + Display { fn description(&self) ->
&str; fn cause(&self) -> Option<&Error>; }
impl Error + 'static { pub fn downcast_ref<T>(&self) -> Option<&T>
where T: Error + 'static; }
— Charles Darwin “To kill std::error is as good a
service as, and sometimes even better than, the establishing of a new trait”
Problems • Generic errors give no guarantees • no Send
/ Sync / Debug • causes() returns non static errors • description() is useless • no backtraces
Enter Failure
— Winston Churchill “Success consists of going from std::error to
failure without loss of enthusiasm”
some std errors are fails nice! impl<E> Fail for E
where E: StdError + Send + Sync + 'static
failure 0.1 ➠ failure 1.0
pub trait Fail: Display + Debug + Send + Sync
+ 'static { fn cause(&self) -> Option<&Fail>; fn backtrace(&self) -> Option<&Backtrace>; fn context<D>(self, context: D) -> Context<D> where D: Display + Send + Sync + 'static, Self: Sized; }
Fail can be derived
#[derive(Fail, Debug)] #[fail(display = "my failure happened")] pub struct MyFailure;
#[derive(Fail, Debug)] #[fail(display = "my failure happened")] pub struct MyFailure
{ backtrace: failure::Backtrace, }
#[derive(Fail, Debug)] #[fail(display = "my failure happened")] pub struct MyFailure
{ backtrace: failure::Backtrace, #[fail(cause)] io_cause: ::std::io::Error, }
Fail & Error
Fail ⟷ Error
Fail for libraries Error for applications
What's in the Package
Main Functionality • Fail trait • Error type • Context
Bonus Points • Fail works with no_std • Fail works
with many std::errors • error-chain is deprecating itself for failure • actix and others are already using it!
— rustc an Error is not a Fail
failure 0.1: error.cause() failure 1.0: error.as_fail() Error to &Fail
Examples
#[derive(Debug, Fail, PartialEq, Eq, PartialOrd, Ord)] #[fail(display = "invalid value
for project id")] pub struct ProjectIdParseError; Parse Errors
#[derive(Debug, Fail)] pub enum DsnParseError { #[fail(display = "no valid
url provided")] InvalidUrl, #[fail(display = "no valid scheme")] InvalidScheme, #[fail(display = "username is empty")] NoUsername, #[fail(display = "no project id")] NoProjectId, #[fail(display = "invalid project id")] InvalidProjectId(#[fail(cause)] ProjectIdParseError), } Complex Parse Errors
fn parse(url: Url) -> Result<Dsn, DsnParseError> { let project_id: i64
= url.path() .trim_matches('/') .parse() .map_err(DsnParseError::InvalidProjectId)?; Ok(Dsn { project_id }) } Mapping Errors
#[derive(Debug, Fail, Copy, Clone, PartialEq, Eq, Hash)] pub enum ErrorKind
{ #[fail(display = "governor spawn failed")] TroveGovernSpawnFailed, #[fail(display = "governor shutdown failed")] TroveGovernShutdownFailed, } Error Kinds
#[derive(Debug)] pub struct Error { inner: Context<ErrorKind>, } Custom Errors
impl Fail for Error { fn cause(&self) -> Option<&Fail> {
self.inner.cause() } fn backtrace(&self) -> Option<&Backtrace> { self.inner.backtrace() } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.inner, f) } } Error Pass Through
pub fn run(config: Config) -> Result<(), Error> { let trove
= Arc::new(Trove::new(config)); trove.govern().context(ErrorKind::TroveGovernSpawnFailed)?; // … } Example Usage
use failure::{Error, ResultExt}; pub fn attach_logfile(&mut self, logfile: &str) ->
Result<(), Error> { let f = fs::File::open(logfile) .context("Could not open logfile")?; let reader = BufReader::new(f); for line in reader.lines() { let line = line?; User Facing with Error
A person who never made a mistake never had to
write an error API
?