3 ms·
I feel like your last point is the exact issue with exceptions, not rust’s errors. Exceptions are like having “?” on every single line.
by mr_00ff00 2y ago
I feel like your last point is the exact issue with exceptions, not rust’s errors. Exceptions are like having “?” on every single line.
- tsimionescu 2y agoWhen an exception happens, you get a stack trace somewhere in your logs (unless you do something really weird). That doesn't always include all the information you'd like (for example, if the error happened in a loop, you don't get info about the loop variable). In contrast, unless you manually add context to the error (or use a library that does something like this for you, overriding the default ? behavior), you won't get any information about where an error occurred at all. Sure, with exceptions, you don't know statically where an exception might happen. But at runtime, you do get the exact information. So, if the error is hard to reproduce, you still have information about where exactly it occurred in those rare occasions where it happened.
- tialaramex 2y ago> When an exception happens, you get a stack trace somewhere in your logs OK, so, if I write the canonical modern C++ Hello World, execute it against an environment where the "standard output" doesn't exist, where does this stack trace get recorded? Maybe it depends on the compiler and standard library implementation somehow? My impression is that in reality C++ just ignores the problem and carries on, so actually there was no stack trace, no logging, it just didn't work and too bad. Unsurprisingly people tasked with making things work prefer a language which doesn't do that.
- HumanOstrich 2y agoHow does any other language deal with POSIX standard I/O streams or the lack thereof? Definitely not a C++ or exceptions problem. Which language lets you compile a "Hello, World!" program and then execute it against a non-POSIX-compatible environment and get the correct output... somewhere? If you're executing against a POSIX-compatible environment, then stdin, stdout, and stderr are expected to exist and be configured properly if you want them to work[1]. If you're executing against some other environment, like webassembly or an embedded system, then you'll already (hopefully) be using some logging and error handling approach that sends output to the correct place. Doesn't matter if you're using C, C++, .NET, Rust, Zig, etc. For example, webassembly is an environment without stdio streams. It's your responsibility to make sure there is a proper way to record output, even if it's just a compatibility layer that goes to console.log. [1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/stdin.html https://pubs.opengroup.org/onlinepubs/9799919799/functions/s...
- tialaramex 2y agoThe other languages do not (as my parent claimed) write stacktraces to a log somehow. I suspect that in reality they've ommitted to explain that they're expected to write all the C++ code to make that stacktrace and write it to a log, but once you add those steps you're back to parity with Rust, the Rust programmers can write a stacktrace to a log too. In the specific case of "Hello, World" it's more embarrassing. The Rust Hello World does indeed experience and report errors if there are any, the canonical C just ignores them, as does the C++.
- HumanOstrich 2y ago> The Rust Hello World does indeed experience and report errors if there are any, the canonical C just ignores them, as does the C++. Can you give an example for each of those?
- tialaramex 2y agohttps://github.com/sunfishcode/hello-world-vs-io-errors?tab=readme-ov-file https://github.com/sunfishcode/hello-world-vs-io-errors?tab=...
- HumanOstrich 2y agoThank you for providing a reference. After reading the blog post on that page, I'm even less convinced that your point is useful. I don't think it's a bug if, like in the C example, you don't handle the return value of the function you are calling. The strace shows that the function returned an error, but the code doesn't check it. Not a language flaw. In fact, in most of the languages that "don't have the bug", the runtime is automagically capturing the issue and aborting the program. Like an exception. Rust just "doesn't have the bug" because the compiler forces you to handle the error. All the .NET languages do the same thing at runtime and force you to handle the I/O error... with an exception handler. Unfortunately, your talking points just seem like more Rust fanaticism trying to discredit any other language. This happens in every single discussion about any language other than Rust, especially C/C++. I'm not going to engage any further.
- tsimionescu 2y agoI'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.
- whytevuhuni 2y agoYou seem to be arguing more for stack traces than for exceptions? Rust can store backtraces in value objects as well [0]. It's opt-in (capturing a stack trace at the error value's creation may be expensive if that error is eventually handled), but with the anyhow crate you get a decent compromise: a stack trace is captured at the boundary of your program and libraries during the conversion, and then shown only if the error bubbles up to main(). And you get the bonus of storing both the stack trace, and relevant context where needed, e.g. to show values of parameters. Here's how that playground example above fails: Error: Second try Caused by: 0: Parsing 'forty-two' as number 1: invalid digit found in string Stack backtrace: 0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from at ./.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.94/src/backtrace.rs:27:14 1: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual at ./.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:2009:27 2: playground::parse_number at ./src/main.rs:25:8 3: playground::parse_and_increment at ./src/main.rs:18:18 4: playground::main at ./src/main.rs:7:19 ... [0] https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=99dbbbc70875df5fdd44d90e2dbf7c69 https://play.rust-lang.org/?version=stable&mode=debug&editio...