Catching panic at FFI boundary, iff unwinding enabled

I am writing a library involving FFI (with C), which needs to pass to a foreign function a pointer to a Rust function, which may panic, and must ergo catch the panic at the boundary, jump over the foreign stack frame, and resume unwinding — if unwinding is enabled. I want this library to be useful either sans runtime, or with it.

Options which come to mind:

  • #[cfg] by panic strategy — to my knowledge no such cfg parametre is
  • define __rust_start_panic and __rust_maybe_catch_panic with weak linkage — no idea whether this would even work, and likely highly unstable

What is the reliable method to catch panic iff unwinding enabled?

For the curious: the library is of a text screen and scrollback buffer, e.g. for a terminal emulator, which may be useful for terminal emulators in both user- and kernel-space, thus my concern about the runtime.

I don't fully understand. You can use catch_unwind both with the abort and the panicking runtime. It will just never catch and return an error with the abort one.

You pass the returned result over the FFI boundary and back and continue panicking if the result holds a panic value. I haven't got a compiler at hand, currently, but in case of the abort runtime, panicking::try (the internal implementation of catch_unwind) should pretty much optimise to Ok(closure.call()).

Anything holding you back from doing that?

I usually define a macro that'll automatically wrap a function in catch_unwind(). There's no need to play around with #[cfg] or linkage.

I also maintain a pretty detailed FFI guide that may be useful to you. I believe there's a section regarding exception safety (preventing panics) under "error handling".

Sorry if this wasn't clear: I want the crate to be utile with no runtime at all, i.e. no_std.

Will it work in both std and no_std? i.e. leave out the catch_unwind in a no_std situation?

This is the essential problem here: how to catch unwinds if we need to, but to not have extra dependencies if we don't.

You mean chapter 7, "Better Error Handling"? I see no such section there.

In this case, you'd have a "no_std" or "use_std" feature anyways, which could be used do at least find out the cases.

I prefer method where you have a "use_std" feature, which is on by default.

In this case, you'd have these cases:

  1. use std: just use the method described above
  2. no std, panic=abort: do nothing
  3. no std, panic=unwind: you'll have to provide the panicking machinery yourself, maybe by reimplementing the parts of std::panicking you need.
  1. and 3. can be implemented in a wrapper type or function that uses the use_std to select its implementation.

TBQH, though, I'd investigate the need a no_std implementation that does proper panic unwind before building it.

Are there even any contexts where you can have unwinding panics without std (short of implementing a custom std for a particular platform, at least)

Well, libstd is in itself an example of such a context.

I mostly care about your cases 1 and 2, at least for now. My concern is, if whether my library catches panics is a function of a feature flag, the user could cause UB with no unsafe blocks, merely by toggling the flag.