Slapping on a `.expect` is also error handling!
-
“An abrupt exit”, more commonly known as a “crash”.
If you’re going to argue that an exit through
panic!()
is not a crash, I will argue that your definition of a crash is just an abrupt exit initiated by the OS. In other words, there’s no meaningful distinction as the result is the same.wrote on last edited by [email protected]I don't think that's a valid comparison. The behavior does differ when it comes to cleanly releasing resources. Rust's panic performs the drop actions for the current values on the stack, a SIGILL or SIGSEGV crash doesn't.
#[derive(Debug)] struct MyStruct {} impl Drop for MyStruct { fn drop(&mut self) { println!("{:?}", "imagine cleanup here"); // this is called } } fn main() { let a = MyStruct {}; panic!("panic!"); println!("{a:?}"); }
-
I don't think that's a valid comparison. The behavior does differ when it comes to cleanly releasing resources. Rust's panic performs the drop actions for the current values on the stack, a SIGILL or SIGSEGV crash doesn't.
#[derive(Debug)] struct MyStruct {} impl Drop for MyStruct { fn drop(&mut self) { println!("{:?}", "imagine cleanup here"); // this is called } } fn main() { let a = MyStruct {}; panic!("panic!"); println!("{a:?}"); }
That’s fair, although technically you could catch SIGSEGV and release resources that way too.
Also, given that resources will be reclaimed by the OS regardless of which kind of crash we’re talking about, the effective difference is usually (but not always) negligible.
Either way, no user would consider a
panic!()
to be not a crash because destructors ran. And most developers don’t either. -
I prefer it over alternatives:
- Exceptions: ”Oh no! Guess I’ll just die”
- Error codes: ”If a non-zero error code is returned but no one notices, is it really an error?”
The crash early, crash often approach of Erlang has made for some amazingly resilient systems.
One time on a project I was working on, some horribly broken code was merged (nobody in the team had even heard of reviewing code). As soon as a specific call was made, it was executed once and then the thread crashed. The only way we noticed was that response times increased with load. All data and behavior was still correct. Whole nodes could go down and all you notice is a dip in performance until it comes back online.
Of course it requires special care in designing. Everything runs in stateless server threads with supervisors restarting them as needed. This in turn requires some language support, like lightweight threads. Our application would happily run tens of thousands of threads on an ancient sparkstation.
-
try { execute.SomeMethod(); } catch(Exception ex) {}
wrote on last edited by [email protected]goto fail;
-
I mean using unwrap is not bad practice if the value is guaranteed to not be none, which can happen frequently in some applications.
wrote on last edited by [email protected]If it's guaranteed to not be
None
, why is it anOption
? -
If it's guaranteed to not be
None
, why is it anOption
?Here's a bad example but hopefully captures the why. https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=43d055381e7bb52569c339d4526818f4
We have a condition we know must be satisfied (the option will always be Some), but cant prove in code.
-
If it's guaranteed to not be
None
, why is it anOption
?wrote on last edited by [email protected]Oh, it can happen when you do calculations with compile-time constants...
But the GP's claim that it's a "frequent" thing is suspect.
(Crashing is also useful when you are writing and-user applications, but you'll probably want .expect like in the meme.)
-
“An abrupt exit”, more commonly known as a “crash”.
If you’re going to argue that an exit through
panic!()
is not a crash, I will argue that your definition of a crash is just an abrupt exit initiated by the OS. In other words, there’s no meaningful distinction as the result is the same.wrote on last edited by [email protected]I was talking more about unwrap causing a panic rather than calling the actual panic macro directly. Rust forces the programmer to deal with bad or ambiguous results, and what that is exactly is entirely decided by the function you are calling. If a function decides to return None when (system timer mod 2 == 0), then you'd better check for None in your code. Edit: otherwise your code is ending now with a panic, as opposed to your code merrily trotting down the path of undefined behaviour and a segfault or similar later on.
Once you get to a point where we are doing the actual panic, well, that is starting to just be semantics.
-
Unwrap is good for prototyping and trying out stuff fast, but it generally shouldn't make it past a code review onto main, unless you're very sure
Exactly.
Personally, I call it "python mode" since you're staying on the "happy path" and let the program just crash out if those expectations aren't met.
-
If it's guaranteed to not be
None
, why is it anOption
?A very typical use-case would be getting something from a HashMap (or a Vector) and calling unwrap because you know it must exist (as you got a reference to the index or object that must be valid in the HashMap or Vector).
Or if you call a function that returnsOption<…>
depending on the current state and you know that it must returnSome(…)
in the current situation.