# \#\[no\_panic\] again

**URL:** <https://internals.rust-lang.org/t/no-panic-again/5350>\
**Category:** language design\
**Created:** [June 2, 2017, 11:54pm UTC](https://internals.rust-lang.org/t/no-panic-again/5350 "2017-06-02T23:54:03Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![stepancheg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stepancheg/32/63_2.png) [@stepancheg](https://internals.rust-lang.org/u/stepancheg)\
**Post date:** [June 2, 2017, 11:54pm UTC](https://internals.rust-lang.org/t/no-panic-again/5350/1 "2017-06-02T23:54:03Z")

</div>

There was a short thread about `#[no_panic]` attribute [in 2015](https://internals.rust-lang.org/t/no-panic/1356).

I’ve recently written some amount of `unsafe` code, and I found it’s hard to make sure code is panic-safe. Code calls some functions and perform potentially-panicing operations like `vec[i]` while manually allocating and releasing memory.

Unsafe code could easily corrupt memory or leak on panic.

Consider an artificial example:

```rust
struct MySmallVec<T> {
    ptr: *mut T,
    len: u8,
    cap: u8,
}

impl<T> MySmallVec<T> {
    unsafe fn update_as_vec<R, F>(&mut self, f: F) -> R
        where F : FnOnce(&mut Vec<T>) -> R
    {
        unsafe {
            let mut v = Vec::from_raw_parts(self.ptr, self.len as usize, self.cap as usize);
            f(&mut v);
            assert!(v.capacity() <= u8::MAX);
            self.ptr = v.as_mut_ptr();
            self.len = v.len() as u8;
            self.cap = v.capacity() as u8;
            mem::forget(v);
        }
    }
}

```

There are hidden problems with this code: if it panics in `f()` or `f` reserves too much memory, destructor of `Vec` is called and and `MySmallVec` object becomes invalid.

But caller can `catch_unwind` and continue working with corrupted memory.

C++ has `noexcept` function attribute, and Rust could have similar `#[no_panic]` attribute:

```rust
    #[no_panic]
    unsafe fn update_as_vec<R, F>(&mut self, f: F) -> R { ... }

```

The program will terminate if code is paniced inside that function. And it is much safer than working with corrupted memory.

Bug in that function can be fixed. However, if function is not intended to panic, author could simply add `#[no_panic]` attribute and sleep better instead of thinking about panic-safety.

Can we have `#[no_panic]` please?

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [June 3, 2017, 12:08am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/2 "2017-06-03T00:08:40Z")

</div>

In your example, couldn’t _you_ use `catch_unwind` around `f(&mut v)` to clean up or abort?

---

<div class="post-metadata">

**Author:** ![stepancheg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stepancheg/32/63_2.png) [@stepancheg](https://internals.rust-lang.org/u/stepancheg)\
**Post date:** [June 3, 2017, 12:17am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/3 "2017-06-03T00:17:52Z")

</div>

> In your example, couldn't you use catch\_unwind around f(&mut v) to clean up or abort?

There are two arguments against it:

1. I want to wrap whole function with it, not just call to `f`

So it will be something like

```rust
    unsafe fn update_as_vec_hidden<R, F>(&mut self, f: F) -> R { ... }

    unsafe fn update_as_vec<R, F>(&mut self, f: F) -> R {
        abort_on_panic(|| self.update_as_vec_hidden(f))
    }

```

for each function, which is too noisy.

1. `catch_unwind` adds runtime overhead, which could be too large for small functions. `#[no_panic]` has no runtime overhead at all.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [June 3, 2017, 12:28am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/4 "2017-06-03T00:28:04Z")

</div>

Another option is to use some sentinal object:

```rust
struct Sentinal;
impl Drop for Sentinal {
    fn drop(&mut self) {
        panic!()
    }
}

```

Create one before your critical section, and `forget` it as you leave.

(I’m not saying `#[no_panic]` is a bad idea – just exploring what’s possible now.)

---

<div class="post-metadata">

**Author:** ![stepancheg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stepancheg/32/63_2.png) [@stepancheg](https://internals.rust-lang.org/u/stepancheg)\
**Post date:** [June 3, 2017, 12:34am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/6 "2017-06-03T00:34:02Z")

</div>

That should work, and likely has no overhead.

It is not as ergonomic as `#[no_panic]` though (for example, you cannot use `?` with it).

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [June 3, 2017, 12:42am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/7 "2017-06-03T00:42:44Z")

</div>

Hmm, I spied before your edit that you suggested `thread::panicking()`, which I didn’t know about. That does seem like a good idea instead of manually calling `forget`, and then `?` should work fine too.

---

<div class="post-metadata">

**Author:** ![stepancheg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stepancheg/32/63_2.png) [@stepancheg](https://internals.rust-lang.org/u/stepancheg)\
**Post date:** [June 3, 2017, 12:45am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/8 "2017-06-03T00:45:12Z")

</div>

`thread::panicking()` is not zero-cost. I actually like your idea about `mem::forget` of `Sentinal`.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [June 3, 2017, 12:51am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/9 "2017-06-03T00:51:27Z")

</div>

FWIW, Rayon has a similar [`AbortIfPanic`](https://github.com/nikomatsakis/rayon/blob/194945535241f3ccafd5144aa9472d50692ae664/rayon-core/src/unwind.rs#L26-L35) which is also used by forgetting.

---

<div class="post-metadata">

**Author:** ![hanna-kruppe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hanna-kruppe/32/6540_2.png) [@hanna-kruppe](https://internals.rust-lang.org/u/hanna-kruppe)\
**Post date:** [June 3, 2017, 11:05am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/10 "2017-06-03T11:05:01Z")

</div>

> [@stepancheg](#):
>
> catch\_unwind adds runtime overhead, which could be too large for small functions. #[no\_panic] has no runtime overhead at all.

Hm, can you elaborate on why? I'd expect `#[no_panic]` to be implemented pretty much like wrapping the whole body in `catch_unwind` and aborting if unwinding was caught. This is also how `noexcept` is implemented in C++, AFAIK.

---

<div class="post-metadata">

**Author:** ![stepancheg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/stepancheg/32/63_2.png) [@stepancheg](https://internals.rust-lang.org/u/stepancheg)\
**Post date:** [June 3, 2017, 11:27am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/11 "2017-06-03T11:27:15Z")

</div>

> [@hanna-kruppe](#):
>
> Hm, can you elaborate on why? I'd expect #[no\_panic] to be implemented pretty much like wrapping the whole body in catch\_unwind and aborting if unwinding was caught. This is also how noexcept is implemented in C++, AFAIK.

`catch_unwind` calls non-inlinable function `__rust_maybe_catch_panic` with callback, so it prevents lots of optimizations.

When compiler handles `#[no_panic]`, compiler could simply emit call to `abort` instead of calls to destructor in that place where it generates code for unwind on panic. So `#[no_panic]` is zero-cost: regular (non-panicking) code is no different from code without `#[no_panic]`.

---

<div class="post-metadata">

**Author:** ![hanna-kruppe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hanna-kruppe/32/6540_2.png) [@hanna-kruppe](https://internals.rust-lang.org/u/hanna-kruppe)\
**Post date:** [June 3, 2017, 9:15pm UTC](https://internals.rust-lang.org/t/no-panic-again/5350/12 "2017-06-03T21:15:55Z")

</div>

> [@stepancheg](#):
>
> catch\_unwind calls non-inlinable function \_\_rust\_maybe\_catch\_panic with callback, so it prevents lots of optimizations.

That might just be a deficit of the current implementation. (Note that LTO can inline that function, but the resulting code still isn't _great_).

> [@stepancheg](#):
>
> When compiler handles #[no\_panic], compiler could simply emit call to abort instead of calls to destructor in that place where it generates code for unwind on panic.

IIUC that only works for panics _directly_ in a `#[no_panic]` function. Most such functions will contain at least a few calls to other code, so there will still be `invoke`s and landing pads (unless you separately compile with `-C panic=abort`, in which case the whole issue is moot anyway).

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [March 25, 2019, 8:28am UTC](https://internals.rust-lang.org/t/no-panic-again/5350/13 "2019-03-25T08:28:37Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
