Hacker News new | ask | show | jobs
by nextaccountic 951 days ago
Rust has panics as well and they appear pretty much everywhere, because the Rust stdlb made a conscious decision to panic on allocation failures (later non-panicking APIs were added, but most people dont use them, and in special, most dependencies will not use this and will panic on random occasions), and also because common operations like integer division and array indexing will panic on bugs (and also integer overflow on debug builds)

In any case OCaml has memory safety, sort of (it has data races but as described in the paper "data races bounded in time and space", data races in OCaml doesn't lead to unrestricted UB like in Rust, C, C++, and most other languages actually) (unless you opt into OCaml's unsafe constructs like Obj.magic, which is like Rust's unsafe without using unsafe {} block), because it has a GC

So when you talk about safety in the context of exceptions, you probably mean exception safety rather than memory safety. Exception safety basically means you code works even if an exception is raised. Which is really hard to assure since as you said, exceptions are everywhere

But Rust suffer from this same problem! In Rust this is called panic safety and mitigating it has taken a great deal of complexity, with things like lock poisoning, which adds overhead but limit the scope of panics in multithreaded programs, and the UnwindSafe trait, which is probably a good attempt but is ignored in most of the ecosystem. Many people think such measures are inadequate and insufficient and prefer to run programs with panic=abort, which means to just terminate the program when there is any panic. (many C++ projects disable exceptions in the same way for example)

Which is kind of unfortunate because now there are many Rust programs that are only correct if you run with panic=abort, and will break if you enable stack unwinding (which is the default)

1 comments

Rust currently cannot reliably panic on allocation failures because the error object is inside a Box, which itself requires allocation. This means that if memory is tight, panicking itself might fail.

For reference, this is the type that is returned from catch_unwind: https://doc.rust-lang.org/std/thread/type.Result.html

But I completely agree that Rust has exceptions. They are even used in the toolchain implementation for non-local control flow.

> Rust currently cannot reliably panic on allocation failures because the error object is inside a Box, which itself requires allocation. This means that if memory is tight, panicking itself might fail.

This seems like something that should be allocated at program startup, just like other things like the program's environment (I think it's copied to Rust's own data structures at startup to avoid using the non-threadsafe C API), and other things allocated at startup

.. but of course, not at Linux, such error allocation would be unneeded there..

.. except of you disable overcommit, which can and do happen, so in the general case you don't know if this error object can ever appear

The Box type, as used in the return type, transfers ownership to the calling function, so a check in the deallocator path would be needed to recognize this special object and avoid deallocating it. GCC has an emergency pool for its exception allocations, which is also quite ugly. And of course that pool can be too small.

It should be possible to add a third arm to that Result type, returning some &'static reference, but I'm not sure how to do it in a backwards-compatibile way.

I mean, allocate something like

static ALLOCATION_ERROR: Box<dyn Error> = (something);

and then use this variable whenever there is an allocation error. you might need unsafe { } but that's okay, the stdlib is full of unsafe

If you use it for producing the Box in the return value, you have to move out of it, and then it's gone. If you don't move it, you have a use-after-free bug after the first such panic has been caught and the box has been dropped as a result.