I'll go into details about your particular question, but I first want to explain why it's missing the point. The difference in terms of logging between exceptions and Rust error handling (or Haskell, or Go, or C) is unrelated to how you print out the log information. It's related to the fact that the exception object itself collects and carries the stack trace information, which the runtime populates if and when an exception happens, whereas in Rust it's up to the programmer (or some library) to manually collect this information and either print it or add it to a custom error object, at every call site. The fact that uncaught exceptions get printed to stdout is the tiniest little bonus, and irrelevant for most programs: you shouldn't have uncaught exceptions in the first place. The important thing is that whenever you catch an exception, you know for sure that you'll have some useful diagnostic information about where exactly it occurred, regardless of who wrote the code between here and there.
Now on to your specific question.
First of all, I explicitly called out C++ exceptions as not having this useful property. C++ exceptions don't collect a stack trace, and the C++ runtime simply exits with an error code if an exception is thrown without a handler.
Now, moving to any other language with exceptions. What happens by default if executing in an environment without stdout will depend on details of the runtime of that language for that environment.
But let's assume that the runtime is not written to handle this gracefully. Here's the entirety of the code you need to add to your exception-based program to handle a lack of stdout and still get stack traces, in pseudo-code:
int main() {
try {
return oldMain();
} catch (Exception e) {
with(File f = openFile("my-log.log")) {
f.write("Unhandled exception:");
e.printStackTrace(f);
}
}
}
Where oldMain() is the main() you'd write for the same program if you did have stdout.