Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is `std::process::exit` O.K. to use?

The documentation of std::process::exit says:

If a clean shutdown is needed it is recommended to only call this function at a known point where there are no more destructors left to run.

Maybe due to my lack of systems programming background, I have no clue if there are destructors left to run at a specific point and if I should care. The only thing that comes to my mind is pending write operations to a file (or to something else) where it's a good idea to leave things in a clean state.

Anything else to watch out for? I suspect it's not advisable to use in larger, more complex programs, but for small tools it seems convenient.

like image 410
Thomas W Avatar asked Aug 30 '16 13:08

Thomas W


1 Answers

Short answer:

  • rather use panic!() in nearly all cases
  • you can pretty much only be sure that no destructors are left to run, if ...
    • ... you are in the main() function
    • ... you manually handle the stack including unwinding (you're probably not...)
  • sometimes you can just ignore destructors when exiting the program anyway, but you need to be careful!

Slightly longer explanation

[...] for small tools it seems convenient.

If you want to exit your program due to an unrecoverable error, the recommend way is to panic!(). This will unwind the stack (run all destructors) and exit the program with additional information (including the message string you can specify).

[...] if there are destructors left to run at a specific point and if I should care.

There are destructors left to run if there are local variables that implement Drop in your current stack frame or in any stack frame above. A stack frame is a region in the stack memory that holds all local variable of a function call (loosely speaking). After a function exits, all local variables discarded which includes calling the destructor of all variables that implement Drop.

The probability that some destructor needs to run grows with the depth of your stack ("how many functions were called between main() and your current stack frame). Therefore it's really hard to reason about this whenever you are not in the main() function.


When do types implement Drop? When it's wrong to just ignore them. Consider an i32: when we exit the function we can just... leave it in stack memory, because it doesn't have any negative side effects (ignoring the special case of data security for a moment). However, there are many types that do need to implement Drop. Here are a few categories:

  • Allocating heap memory: Box<T>, Vec<T>, HashMap<T>, ...
  • Holding OS resources: File, Socket, ...
  • Returning handles: Ref, MutexGuard, ...
  • ...

Not running the destructor for those has different effects. The first group is probably the most harmless one: we will leak memory. But when you exit your program, the operating system will clean up all this memory anyway. Nearly the same goes for OS resources: file descriptor are usually deleted by the operating system when a program exits.

But there are more reasons to run a destructor. The important point to consider: you often don't know why certain types implement Drop, but these types do rely on it. Certain unwanted things can happen when you ignore this. Often you won't really notice, but sometimes it can lead to serious problems.

like image 142
Lukas Kalbertodt Avatar answered Nov 19 '22 16:11

Lukas Kalbertodt