Live data from Hacker News

No-Panic Rust: A Nice Technique for Systems Programming

blog.reverberate.org

131–140 of 143 posts

Re: No-Panic Rust: A Nice Technique for Systems Programming

#131
post #35

Earlier quoted context omitted.

The thing with functional programming (specifically, immutable data,) is that as long as the invalid state is immutable, you can just back up to some previous caller, and they can figure out whether to deal with it or whether to reject up the its previous caller. This is why Result (or Maybe, or runExceptT, and so on in other languages) is a perfectly safe way of handling unexpected or invalid data. As long as you en…

The program can detect invalid state, but your intention was to never get to that state in the first place. The fact that the program arrived there is a Logic error in your program. No amount of runtime shenanigans can repair it because the error exists without your knowledge of where it came from. You just know it's invalid state and you made a mistake in your code. The best way to handle this is to crash the progra…

> The program can detect invalid state, but your intention was to never get to that state in the first place. The fact that the program arrived there is a Logic error in your program. No amount of runtime shenanigans can repair it because the error exists without your knowledge of where it came from.

True: a program can't fix an internal assertion error, and the failed component might not be recoverable without a reset. But that doesn't means that the whole program is doomed. If the component was optional the program might still work although with reduced functionality. Consider this: way you do not stop the whole computer if a program aborts.

As I mentioned elsethread, in unsafe languages an assertion error might, with high probabilty, be due to the whole runtime being compromised, so a process abort is the safest option.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#132

Earlier quoted context omitted.

> I would say that you are using them incorrectly if you assume them as recoverable. no it's them being recoverable at well defined boundaries is a _fundamental_ design aspect of rust > Overhead comes from the cleaning process. If you don't clean properly, you might leak information or allocate more resources than you should. and that same cleanup process makes it recoverable

There is a social norm to treat panics as unrecoverable (in most cases — some do use panics to perform cancellation in non-async code).

they are still anyway designed to be recoverable as a fundamental aspect of rust

so that e.g. you web server doesn't fall over just because one request handler panics

and it still is fundamental required that any code is safe in context of panic recovery (UnwindSafe is misleading named and actually not about safety, anything has to be "safe" in context of unwind, UnwindSafe just indicates that it's guaranteed to have sensible instead of just sound behavior in rust)

people over obsessing with panic should be unrecoverable is currently IMHO on of the biggest problems in rust not in line with any of it's original design goals or how panics are in the end design to work or how rust is used in many places in production

yes they are not "exceptions" in the sense that you aren't supposed to have fine grained recoverablility, but recoverability anyway

without recoverable panics you wouldn't be able to write (web and similar) servers in a reasonable robust way in rust without impl some king of CGI like pattern, which would be really dump IMHO and is one of the more widely used rust use cases

Re: No-Panic Rust: A Nice Technique for Systems Programming

#133
post #35

Earlier quoted context omitted.

The thing with functional programming (specifically, immutable data,) is that as long as the invalid state is immutable, you can just back up to some previous caller, and they can figure out whether to deal with it or whether to reject up the its previous caller. This is why Result (or Maybe, or runExceptT, and so on in other languages) is a perfectly safe way of handling unexpected or invalid data. As long as you en…

> This is why Result (or Maybe, or runExceptT, and so on in other languages) is a perfectly safe way of handling unexpected or invalid data. They are great for handling expected errors that make sense to handle explicitly. If you try to wrap up any possible error that could ever happen in them you will generate horrendous code, always having to unwrap things, everything is a Maybe. No thanks. I know it is tempting to…

The monad and lifting fixes this problem of having to unroll the maybe type. But this is an advanced abstraction.

I actually disagree with this. Use the maybe type religiously, even without the monad because it prevents errors via exhaustive matching.

The exception should only be used if your program detects a bug.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#134
post #99

Earlier quoted context omitted.

I wonder if there is a simple way to stop compilation quickly, with a readable error message, if the optimizer isn't able to eliminate the check. edit: There is https://github.com/dtolnay/no-panic

> Functions that require some amount of optimization to prove that they do not panic may no longer compile in debug mode after being marked #[no_panic]. So you’re probably going to have to protect this with a cfg_attr to only apply the no_panic in release only.

Probably, yes.

That or forcing compilation to always target release.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#135

Earlier quoted context omitted.

An assert is just a fancy `if condition { panic(message) }`. If the optimizer can show that condition is always false, the panic is declared as dead code and eliminated. The post uses that to get the compiler to remove all panics to reduce the binary size. But you can also just check if panic code was generated (or associated code linked in), and if it was then the optimizer wasn't able to show that your assert can't…

To be clear as there’s a lot of nuance. Assert unchecked is telling the compiler the condition must always hold. The optimizer and compiler don’t make any assumption about the assert. That information is then used by the compiler to optimize away checks it otherwise would have to do (eg making sure an Option is Some if you call unwrap). If you have an assumption that gives unhelpful information, the optimizer will em…

> Wrapping it in a safe call as in this article would have been unthinkable - the unsafe needs to live exactly where you are making the assumption, there’s no safety provided by the wrapper.

I want to be sure I understand your meaning. In your analysis, if the check_invariant function was marked unsafe, would the code be acceptable in your eyes?

Re: No-Panic Rust: A Nice Technique for Systems Programming

#136

Earlier quoted context omitted.

Panic-analyzer looks like it is based on heuristics, searching for known-panicing APIs. I tried it on a workspace that uses io::stdout() and it did not flag this as potentially panicing. No-panic looks nifty: it appears to be reliable, which is great. I wish there was an easy way to automatically apply this annotation to every single function in a given file or crate.

I think the article is wrong in that std::io::stdout would be panicking, or that "the panic is reachable somehow". It's just the optimizer doesn't see it doesn't panic. https://doc.rust-lang.org/beta/src/std/io/stdio.rs.html#674-... The implementation calls indeed a panicking API, OnceLock::get_or_init: https://doc.rust-lang.org/beta/std/sync/struct.OnceLock.html... But it only panicks if it is being used in a wrong…

Thanks for the clarification on OnceLock::get_or_init. It's interesting to know that this is dynamically unreachable, but in a way that the compiler could not statically prove.

I 100% agree with your last paragraph. I would love if the language and stdlib made it easier to make panic-free binaries.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#137

Earlier quoted context omitted.

There is a social norm to treat panics as unrecoverable (in most cases — some do use panics to perform cancellation in non-async code).

they are still anyway designed to be recoverable as a fundamental aspect of rust so that e.g. you web server doesn't fall over just because one request handler panics and it still is fundamental required that any code is safe in context of panic recovery (UnwindSafe is misleading named and actually not about safety, anything has to be "safe" in context of unwind, UnwindSafe just indicates that it's guaranteed to have…

At Oxide we ship our binaries, including our HTTP servers, with panic = abort, fwiw.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#138
post #49
post #27

Earlier quoted context omitted.

You could do it, but I would prefer guarantees on a per-call chain basis using a sanitizer. It should be quite easy to write.

I'm no rustc expert, but from what little I know it seems like disabling panics for a crate would be an obvious first step. You make a great point though. Turning that into a compiler assertion of "this function will never panic" would also be useful.

It’s a good first step, but half of the crates in crates.io have at least 40 transitive dependencies. Some have hundreds or thousands. A big effort.

Re: No-Panic Rust: A Nice Technique for Systems Programming

#139

Earlier quoted context omitted.

they are still anyway designed to be recoverable as a fundamental aspect of rust so that e.g. you web server doesn't fall over just because one request handler panics and it still is fundamental required that any code is safe in context of panic recovery (UnwindSafe is misleading named and actually not about safety, anything has to be "safe" in context of unwind, UnwindSafe just indicates that it's guaranteed to have…

At Oxide we ship our binaries, including our HTTP servers, with panic = abort, fwiw.

I know some do.

But as long as you don't do anything unusual you are basically introducing a (potentially huge) availability risk to your service for no reason but except not liking panics.

Like it's now enough for there to be a single subtle bug causing a panic to have a potentially pretty trivially exploitable and cheap DoS attack vector. Worse this might even happen accidentally. Turning a situation where some endpoints are unavailable due to a bug into one where your servers are constantly crashing.

Sure you might gain some performance, but for many use cases this performance is to small to reason in favor of this decisions.

Now if you only have very short lived calls, and not too many parallel calls in any point in time, and anyway spread scaling across many very very small nodes it might not matter that you might kill other requests, but once that isn't the case it seems to most times be a bad decision.

It also doesn't really add security benefits, maybe outside of you having very complicated in memory state or similar which isn't shared across multiple nodes through some form of db (if it is, you anyway have state crossing panics, or in your case service restarts).

Re: No-Panic Rust: A Nice Technique for Systems Programming

#140

Earlier quoted context omitted.

At Oxide we ship our binaries, including our HTTP servers, with panic = abort, fwiw.

I know some do. But as long as you don't do anything unusual you are basically introducing a (potentially huge) availability risk to your service for no reason but except not liking panics. Like it's now enough for there to be a single subtle bug causing a panic to have a potentially pretty trivially exploitable and cheap DoS attack vector. Worse this might even happen accidentally. Turning a situation where some end…

Well, no it's not just not liking panics — it's that panics can leave memory in an inconsistent state. std::sync::Mutex does poisoning, but many other mutexes don't. And beyond that, you could also panic in the middle of operating on an &mut T, while state is invalid.

Tearing down the entire process tends to be a pretty safe alternative.

Post reply on HN