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
1k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Failing in Rust
A quick talk at a meetup about using failure.
Armin Ronacher
April 24, 2018
More Decks by Armin Ronacher
See All by Armin Ronacher
Reflections on building cloud and local hybrid machine entities
mitsuhiko
1
99
Agentic Coding: The Future of Software Development with Agents
mitsuhiko
0
900
Do Dumb Things
mitsuhiko
0
990
No Assumptions
mitsuhiko
0
430
The Complexity Genie
mitsuhiko
0
350
The Catch in Rye: Seeding Change and Lessons Learned
mitsuhiko
0
450
Runtime Objects in Rust
mitsuhiko
0
440
Rust at Sentry
mitsuhiko
0
610
Overcoming Variable Payloads to Optimize for Performance
mitsuhiko
0
310
Other Decks in Programming
See All in Programming
Foundation Models frameworkで画像分析
ryodeveloper
1
630
Cloudflare is Agents
chimame
0
170
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
810
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
230
2年かけて Deno に DOMMatrix を実装した話 / How I implemented DOMMatrix in Deno over two years
petamoriken
0
210
Built Our Own Background Agent at LayerX
layerx
PRO
10
5.7k
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
700
Go言語とトイモデルで学ぶTransformerの気持ち / fukuokago23-transformer
monochromegane
0
180
SlackアプリとLambdaの 連携を構築した話
pawn_4_s
1
120
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
1
1.8k
AI Readyの正体はデータマネジメントだ メダリオン2.0の最前線
freee
PRO
0
300
in-process GraphQL のすすめ #ginzajs
izumin5210
4
1.4k
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Documentation Writing (for coders)
carmenintech
77
5.5k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
470
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
210
The Limits of Empathy - UXLibs8
cassininazir
1
610
Ruling the World: When Life Gets Gamed
codingconduct
0
300
Utilizing Notion as your number one productivity tool
mfonobong
4
550
GitHub's CSS Performance
jonrohan
1033
470k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
290
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
?