Hello all!
I have made some smaller programs in rust and I really love the language.
I have one thing that I can not get my head around.
I read somewhere that if I in the middle of a function exit my program, using exit, panic!, assert_eq! or similar rust does not clean up memory. To be able to clean up the right way you have to return to main and exit there.
Example:
fn main () {
another_fn();
}
fn another_fn() {
for x in y {
calculating......
if error {
println!("There was an error");
exit(2); //Program exits but is memory cleaned up?
}
}
}
I have been using:
fn main () {
exit(another_fn()); // Memory is cleaned up the right way
}
fn another_fn() -> i32 {
let mut exit_code: i32 = 0;
for x in y {
calculating......
if error {
println!("There was an error");
exit_code = 2;
}
}
exit_code
}
Have I understood it the right way?
Am I supposed to exit in main or is memory cleaned up the correct way exiting "in the middle"?
Should probably define abnormal exit to avoid confusion. My understanding is that a panic unwinds and any Drop::drop are executed along the way (assuming a panic handler doesn't panic itself, which I believe causes an abort that stops unwinding).
It won't leak memory, but suddenly quitting the whole program may turn out to be problematic for many other reasons (unsaved data, incomplete output, or just plain annoying to users if pressing one wrong key crashes the whole app).
You should gracefully propagate errors from functions. With plain error codes this may be tedious, but if instead of fn() -> i32 returning an error code you use fn() -> Result<(), i32> (function returning Ok(()) or Err(exit_code)) you will be able to use the ? operator to simplify calling of such functions (in all places… except main(). There use match).
You're right, that was quite unspecific, and even some of the ways aren't necessarily "abnormal". Off the top of my head, here are ways to exit without unwinding:
Panic during an unwind causes an abort, as you mentioned.
This seems reasonable, is it standard practice that all functions should be returning Result<T,i32> if I want to be able to exit cleanly? If so I don't see it mentioned in the rust book second edition (yet, I'm on chapter 11). If this is the approach you're expected to use it seems like it should have been mentioned somewhere in there by now.
I'm here because I have a program that doesn't have any data I want to save and the user requested a final exit, but I would like all the scopes to close before it exits, I'm not sure I trust Windows to clean everything up properly
Using Result is definitely standard, recommended behaviour, though i32 as the Err type is not optimal (you can use any type you want as Error, including custom enums).
I recommend looking up the failure crate, and the accompanying blogposts. It represents the current "Rolls Royce" of error handling.
It is usually overkill for simple programs, but it does show you the absolute best practice.
Other excellent Rust error libraries are error_chain and quick-error. These represent older, more lightweight solutions.
It’s interesting you mention failure because there was a recent reddit thread on it that didn’t paint its picture as very rosey, and I’m actually unsure if people should be switching to/using it at the moment.
This probably warrants a separate thread for more in-depth discussion of this but thought I’d mention it since you alluded to it.
Note that Result is not special, and it doesn't do anything related to any cleanup of anything. It's just a small struct. A fancy way to return an int from a function.
If you abort() or exit() the process, then all functions will be immediately aborted, regardless of what they return or not.
All OS managed resources will be cleaned up by the OS when your process terminates for whatever reason. All NT objects, all user32 objects, all gdi32 objects, all memory allocations, all will be cleaned up because the OS keeps track of what process owns them.
Any sort of cleanup that is not destroying an OS managed resource however can easily fail to occur if the process terminates abnormally (or even the process exiting normally before a thread could finish).