# Rust has no Exceptions?

**URL:** <https://discourse.haskell.org/t/rust-has-no-exceptions/10729>\
**Category:** Learn\
**Created:** [November 13, 2024, 1:10pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729 "2024-11-13T13:10:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mmport80](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mmport80/32/308_2.png) [@mmport80](https://discourse.haskell.org/u/mmport80)\
**Post date:** [November 13, 2024, 1:10pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/1 "2024-11-13T13:10:14Z")

</div>

I am learning Rust.

There are ‘panics’, and there are well known functions with which you can shoot yourself in the foot with them…

But apparently idiomatic code more or less assumes reliable type definitions… I have to say this is v nice, and something I wish Haskell put (even) more attention on.

In this small aspect I think Rust (being modern) is ahead of Haskell (maybe I should switch to a better Prelude : ). And for beginners a big win, imo.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [November 13, 2024, 2:21pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/2 "2024-11-13T14:21:37Z")

</div>

Why better? I think it’s significantly worse. Naturally, untracked exceptions in naked `IO` are a pain in Haskell, but we now have IO-based effect systems (Bluefin and effectful) that can track native `IO` exceptions in the type system. The best of both worlds!

---

<div class="post-metadata">

**Author:** ![emlautarom1](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/emlautarom1/32/4080_2.png) [@emlautarom1](https://discourse.haskell.org/u/emlautarom1)\
**Post date:** [November 13, 2024, 2:24pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/3 "2024-11-13T14:24:06Z")

</div>

Wait until you need to work in a non trivial codebase and you’ll see the amount of work required to manage errors as values. I’ve been there and I much rather work with exceptions.

---

<div class="post-metadata">

**Author:** ![f-a](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/f-a/32/2740_2.png) [@f-a](https://discourse.haskell.org/u/f-a)\
**Post date:** [November 13, 2024, 2:41pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/4 "2024-11-13T14:41:38Z")

</div>

“Errors as values” is something like `Either FailReason a` right?  
I am curious, what is wrong with it?

---

<div class="post-metadata">

**Author:** ![eldritch-cookie](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/eldritch-cookie/32/4130_2.png) [@eldritch-cookie](https://discourse.haskell.org/u/eldritch-cookie)\
**Post date:** [November 13, 2024, 3:42pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/5 "2024-11-13T15:42:31Z")

</div>

to arrive at a problem you need to assume 2 reasonable things

1. a majority of actions can potentially fail and
2. they can fail differently

1 forces you to use the Monad instance of Either or create a case pyramid and live your life on indentation level 20  
2 prohibits you from using the monad instance without a lot of wrapping/unwrapping

so after this you either use exceptions(which are baked in the language and hence free) or polymorphic variants(we don’t have language support like OCAML but there are libraries)

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [November 13, 2024, 4:11pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/6 "2024-11-13T16:11:43Z")

</div>

Bear in mind that the fanciest type systems sometimes lie to you. Things can fail where you don’t expect them to be able to fail. Try:

```haskell
{-# LANGUAGE MagicHash #-}
import GHC.Prim
import GHC.Types
let (I# x) = 1 in let (I# z) = 0 in I# (quotInt# x z)

```

Background: `x`, `z` and `quotInt# x z` are members of an _unlifted_ type `Int#` which is said not to contain bottom.

---

<div class="post-metadata">

**Author:** ![eldritch-cookie](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/eldritch-cookie/32/4130_2.png) [@eldritch-cookie](https://discourse.haskell.org/u/eldritch-cookie)\
**Post date:** [November 13, 2024, 4:33pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/7 "2024-11-13T16:33:04Z")

</div>

sorry it isn’t clear to me how this is related to my comment, when looking at the primops(which are all internal) they clearly specify which can fail with a unchecked exception. sure you can craft a malicious value that crashes the runtime system but that isn’t the game, programming and science in general is cooperative.  
your comment is the equivalent of interrupting someone discussing legal minutia to enlighten them about the many uses of a gun.

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [November 13, 2024, 4:54pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/8 "2024-11-13T16:54:39Z")

</div>

Two possible ways in which exceptions might improve upon errors-as-values for certain cases:

- Exceptions make it easier for layers of code to communicate without the layers inbetween knowing about it. Two articles:

- For Haskell in particular, asynchronous exceptions are at the base of useful libraries like [async](https://hackage.haskell.org/package/async). Concurrency in an hypothetical Haskell that didn’t have async exceptions would look very different.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [November 13, 2024, 5:16pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/9 "2024-11-13T17:16:58Z")

</div>

> [@danidiaz](#):
>
> [“Exceptions Are Shared Secrets”](https://existentialtype.wordpress.com/2012/12/03/exceptions-are-shared-secrets/) by Robert Harper. “the perennial suggestion to put exception-raising information into types makes no sense to me”

I don’t find this one very compelling. If you have the ability to abstract over effects, and hide internals, then it’s a non-problem. You can do it easily with monad transformers, for example

```haskell
newtype MyMonadT m a =
    MkMyMonadT (ExceptT Foo m a)

```

and then no one in between the thrower and the catcher (without access to `MyMonadT`’s definition) can intercept the exception. The story is even better in proper effect systems.

---

<div class="post-metadata">

**Author:** ![chreekat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/chreekat/32/2669_2.png) [@chreekat](https://discourse.haskell.org/u/chreekat)\
**Post date:** [November 13, 2024, 7:41pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/10 "2024-11-13T19:41:18Z")

</div>

Pardon my ignorance, but how do you deal with asynchronous exceptions when you don’t have exceptions?

I know e.g. with Unix processes you can have signals and signal handlers. That’s just another color for the bikeshed, though. There’s always got to be _some_ way to handle real, no-shit exceptions that break the IO abstraction barrier. If not exceptions as we currently have them, wouldn’t you still need _some_ mechanism that would end up with a slightly different set of tradeoffs?

---

<div class="post-metadata">

**Author:** ![chrisdone](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/chrisdone/32/1408_2.png) [@chrisdone](https://discourse.haskell.org/u/chrisdone)\
**Post date:** [November 13, 2024, 9:02pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/11 "2024-11-13T21:02:06Z")

</div>

I think there’s room for both. Haskell lets you do both. Failures as return values is nice in some cases like parsers and whatnot. Failures as exceptions is nice when you’ve got a load of I/O based code that can fail in myriad ways and you don’t care to enumerate them nor would benefit in doing so.

At first I liked Rust’s angle until I saw people using it at work and saw that it’s actually an inconvenience: people sprinkle “anyhow” everywhere as a way to avoid having to produce a well typed error, and every function is littered with “?” on almost every line to the point of meaninglessness. Java has checked exceptions, and not everybody is too happy about them.

Haskell lets you be blasé like Erlang (throw and forget) or meticulous like Java/Rust, depending on your discretion. It’s not perfect but I feel lucky to have it.

MzScheme differentiated exceptions from termination of a thread by a custodian ([Custodians](http://download.plt-scheme.org/doc/053/mzscheme/node119.htm)) that couldn’t be stopped, IIRC. That was really neat. And Common Lisp folk use restarts ([Beyond Exception Handling: Conditions and Restarts](https://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts)) all the time to provide a developer experience, which is another island of behavior quite far away from Haskell/Rust.

---

<div class="post-metadata">

**Author:** ![arybczak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/arybczak/32/2527_2.png) [@arybczak](https://discourse.haskell.org/u/arybczak)\
**Post date:** [November 13, 2024, 9:17pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/12 "2024-11-13T21:17:17Z")

</div>

> Pardon my ignorance, but how do you deal with asynchronous exceptions when you don’t have exceptions?

That’s the thing, Rust (and Go) only pretend they don’t have exceptions. Rust has `panic` and `catch_unwind`, Go has `panic` and `recover`.

I consider this pretense to be a massive flaw of these languages.

I agree with @chrisdone, sometimes you want unchecked exceptions, sometimes you want to track them in types. It generally depends on the use case and it’s nice that Haskell lets you do both. The problem with this is that it’s subtle and IME people don’t like this answer, they expect to be given a simple solution to a complex problem and both Rust and Go pretend they have it, but in practice it results in tons of noise in the code from manual passing of errors downstream everywhere (it’s a hot mess in Go in particular, at least Rust has some syntactic sugar for it).

---

<div class="post-metadata">

**Author:** ![tbidne](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tbidne/32/2949_2.png) [@tbidne](https://discourse.haskell.org/u/tbidne)\
**Post date:** [November 13, 2024, 9:27pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/13 "2024-11-13T21:27:12Z")

</div>

Matt Parsons has a nice blog post describing the main pain point when no exception mechanism exists (or its use is verboten in a given code base): [The Trouble with Typed Errors](https://www.parsonsmatt.org/2018/11/03/trouble_with_typed_errors.html)

To make typed errors work well (i.e. compose) you really need open variants. Unfortunately it’s a rare feature, and bringing it to haskell is [hard](https://github.com/ghc-proposals/ghc-proposals/pull/180).

---

<div class="post-metadata">

**Author:** ![DavidB](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/davidb/32/3616_2.png) [@DavidB](https://discourse.haskell.org/u/DavidB)\
**Post date:** [November 13, 2024, 9:44pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/14 "2024-11-13T21:44:07Z")

</div>

My main gripe with exceptions that are not tracked in the type is that they make it more complicated to include linear logic features. If you have a file handle or channel that needs to be closed, or memory that needs to be freed, then we want to guarantee that this happens in every code path. And the possibility of untracked exceptions makes this more difficult. This is a problem for both Haskell and Rust. Nikos describes the problem for Rust in [Must move types · baby steps](https://smallcultfollowing.com/babysteps/blog/2023/03/16/must-move-types/) and Arnaud for Linear Haskell here: [On linear types and exceptions](https://www.tweag.io/blog/2020-02-19-linear-type-exception/)

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [November 13, 2024, 9:47pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/15 "2024-11-13T21:47:44Z")

</div>

> [@tbidne](#):
>
> To make typed errors work well (i.e. compose) you really need open variants.

I think this is a misconception. Effect systems can compose exception types without open variants by (something isomorphic to) passing them in as arguments. For example, to get the “sum” of errors `Foo` and `Bar` you can use in Bluefin

```haskell
(e1 :> es, e2 :> es) =>
Exception Foo e1 ->
Exception Bar e2 ->
Eff es r

```

or in `effectful`

```haskell
(Error Foo :> es, Error Bar :> es) =>
Eff es r

```

This is perfectly composable, no new type system features necessary.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [November 13, 2024, 9:53pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/16 "2024-11-13T21:53:25Z")

</div>

This is independent of whether they’re tracked in the type, isn’t it? For example, you can consider `IO` as `ExceptT SomeException UIO`, where `UIO` is a monad that doesn’t throw exceptions. I think the problem is having _any_ throw operation where you can write

```haskell
do
  r <- acquire
  throw MyException
  release r

```

and abort the execution of the block before `release`. This is so regardless of whether the do block is running in `IO`, `ExceptT MyException SomeOtherMonad`, or anything else.

---

<div class="post-metadata">

**Author:** ![tbidne](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tbidne/32/2949_2.png) [@tbidne](https://discourse.haskell.org/u/tbidne)\
**Post date:** [November 13, 2024, 10:00pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/17 "2024-11-13T22:00:45Z")

</div>

Tbh I have yet to try that out in earnest. I recall using a similar feature in the past that also relied on constraints (I think `MonadError` + classy prisms). But I found it difficult to use at the time.

Your example looks intriguing, thanks!

---

<div class="post-metadata">

**Author:** ![DavidB](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/davidb/32/3616_2.png) [@DavidB](https://discourse.haskell.org/u/DavidB)\
**Post date:** [November 13, 2024, 10:17pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/18 "2024-11-13T22:17:10Z")

</div>

We probably mean the same thing, I was just too concise in my comment 🙂 I am not too worried about a `throw` operation that just crashes the program if some invariant has been broken, but only about `throw` operations that can be handled, since only these introduce a new control flow mechanism that can break linear logic reasoning. Through your work on bluefin I think you probably agree that exceptions should be handled lexically (i.e. the handler responsible for catching the exception should be determined by lexical scope, not dynamic scope), so a sensible `throw` operation needs to also be passed a capability, and this capability/continuation must be tracked in the type system, which makes it possible again to use linear logic reasoning.

---

<div class="post-metadata">

**Author:** ![eldritch-cookie](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/eldritch-cookie/32/4130_2.png) [@eldritch-cookie](https://discourse.haskell.org/u/eldritch-cookie)\
**Post date:** [November 13, 2024, 11:02pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/19 "2024-11-13T23:02:24Z")

</div>

there are a ton of open variants libraries one of them is [vary](https://hackage.haskell.org/package/vary) sure i guess it isn’t as nice as if it were a language feature but it certainly works.

---

<div class="post-metadata">

**Author:** ![tbidne](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tbidne/32/2949_2.png) [@tbidne](https://discourse.haskell.org/u/tbidne)\
**Post date:** [November 13, 2024, 11:14pm UTC](https://discourse.haskell.org/t/rust-has-no-exceptions/10729/20 "2024-11-13T23:14:43Z")

</div>

Indeed, I am also aware of [variant](https://hackage.haskell.org/package/variant) (ghcup uses it, for what it’s worth). I have always balked at the (perceived?) complexity, however.

[Next page](https://discourse.haskell.org/t/rust-has-no-exceptions/10729.md?page=2)
