$30 off During Our Annual Pro Sale. View Details »
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
990
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
Agentic Coding: The Future of Software Development with Agents
mitsuhiko
0
410
Do Dumb Things
mitsuhiko
0
800
No Assumptions
mitsuhiko
0
300
The Complexity Genie
mitsuhiko
0
270
The Catch in Rye: Seeding Change and Lessons Learned
mitsuhiko
0
380
Runtime Objects in Rust
mitsuhiko
0
370
Rust at Sentry
mitsuhiko
0
530
Overcoming Variable Payloads to Optimize for Performance
mitsuhiko
0
250
Rust API Design Learnings
mitsuhiko
0
610
Other Decks in Programming
See All in Programming
大体よく分かるscala.collection.immutable.HashMap ~ Compressed Hash-Array Mapped Prefix-tree (CHAMP) ~
matsu_chara
2
220
まだ間に合う!Claude Code元年をふりかえる
nogu66
5
830
ViewファーストなRailsアプリ開発のたのしさ
sugiwe
0
470
Github Copilotのチャット履歴ビューワーを作りました~WPF、dotnet10もあるよ~ #clrh111
katsuyuzu
0
110
ハイパーメディア駆動アプリケーションとIslandアーキテクチャ: htmxによるWebアプリケーション開発と動的UIの局所的適用
nowaki28
0
420
これならできる!個人開発のすゝめ
tinykitten
PRO
0
110
20 years of Symfony, what's next?
fabpot
2
360
愛される翻訳の秘訣
kishikawakatsumi
3
320
AIコーディングエージェント(Manus)
kondai24
0
180
Cap'n Webについて
yusukebe
0
130
AIエージェントを活かすPM術 AI駆動開発の現場から
gyuta
0
410
【Streamlit x Snowflake】データ基盤からアプリ開発・AI活用まで、すべてをSnowflake内で実現
ayumu_yamaguchi
1
120
Featured
See All Featured
A Modern Web Designer's Workflow
chriscoyier
698
190k
Side Projects
sachag
455
43k
Fireside Chat
paigeccino
41
3.7k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.6k
Learning to Love Humans: Emotional Interface Design
aarron
274
41k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.6k
Designing for Performance
lara
610
69k
Building an army of robots
kneath
306
46k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
How to train your dragon (web standard)
notwaldorf
97
6.4k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Producing Creativity
orderedlist
PRO
348
40k
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
?