Live data from Hacker News

Matt Godbolt sold me on Rust by showing me C++

collabora.com

201–210 of 675 posts

Re: Matt Godbolt sold me on Rust by showing me C++

#201
post #30

Earlier quoted context omitted.

Aside from the obvious bit size changes (e.g. i8 -> i16 -> i32 -> i64, or f32 -> f64), there is no "hierarchy" of types. Not all ints are representable as floats. u64 can represent up to 2^64 - 1, but f64 can only represent up to 2^53 with integer-level precision. This issue may be subtle, but Rust is all about preventing subtle footguns, so it does not let you automatically "promote" integers to float - you must be…

> Aside from the obvious bit size changes (e.g. i8 -> i16 -> i32 -> i64, or f32 -> f64), there is no "hierarchy" of types. Depends on what you want from such a hierarchy, of course, but there is for example an injection i32 -> f64 (and if you consider the i32 operations to be undefined on overflow, then it’s also a homomorphism wrt addition and multiplication). For a more general view, various Schemes’ takes on the “…

Virgil allows the maximum amount of implicit int->float injections that don't change values and allows casts (in both directions) that check if rounding altered a value. It thus guarantees that promotions and (successful) casts can't alter program behavior. Given any number in representation R, promotion or casting to type N and then casting back to R will return the same value. Even for NaNs with payloads (which can happen with float double).

Re: Matt Godbolt sold me on Rust by showing me C++

#202
post #8

The one thing that sold me on Rust (going from C++) was that there is a single way errors are propagated: the Result type. No need to bother with exceptions, functions returning bool, functions returning 0 on success, functions returning 0 on error, functions returning -1 on error, functions returning negative errno on error, functions taking optional pointer to bool to indicate error (optionally), functions taking r…

I work in a new-ish C++ codebase (mid-2021 origin) that uses a Result-like type everywhere (folly::Expected, but you get std::expected in C++23). We have a C pre-processor macro instead of `?` (yes, it's a little less ergonomic, but it's usable). It makes it relatively nice to work in.

That said, I'd prefer to be working in Rust. The C++ code we call into can just raise exceptions anywhere implicitly; there are a hell of a lot of things you can accidentally do wrong without warning; class/method syntax is excessively verbose, etc.

Re: Matt Godbolt sold me on Rust by showing me C++

#203

Earlier quoted context omitted.

> The C++ code I write these days Meaning you're in a context where you have control on the C++ code you get to write. In my company, lots of people get to update code without strict guidelines. As a result, the code is going to be complex. I'd rather have a simpler and more restrictive language and I'll always favor Rust projects to C++ ones.

That is easy to say today, but I guarantee in 30 year Rust will have rough edges too. People always want some new feature and eventually one comes in that cannot be accommodated nicely. Of course it will probably not be as bad as C++, but still it will be complex and people will be looking for a simpler language.

How many rough edges will C++ have in another 30 years?

Re: Matt Godbolt sold me on Rust by showing me C++

#204
post #89

Earlier quoted context omitted.

The result type does make for some great API design, but SerenityOS shows that this same paradigm also works fine in C++. That includes something similar to the ? operator, though it's closer to a raw function call. SerenityOS is the first functional OS (as in "boots on actual hardware and has a GUI") I've seen that dares question the 1970s int main() using modern C++ constructs instead, and the API is simply a lot b…

Google has been doing a very similar, but definitely somewhat uglier, thing with StatusOr and Status (as seen in absl and protobuf) for quite some time. A long time ago, there was talk about a similar concept for C++ based on exception objects in a more "standard" way that could feasibly be added to the standard library, the expected class. And... in C++23, std::expected does exist[1], and you don't need to use excep…

Facebook's Folly has a similar type: folly::Expected (dating to 2016).

Re: Matt Godbolt sold me on Rust by showing me C++

#205

Yes, Rust is better. Implicit numeric conversion is terrible. However, don't use atoi if you're writing C++ :-). The STL has conversion functions that will throw, so separate problem.

Forcing people to explicitly casts everything all the time means that dangerous casts don't stand out as much. That's an L for rust imo.

Re: Matt Godbolt sold me on Rust by showing me C++

#206
post #89

Earlier quoted context omitted.

The result type does make for some great API design, but SerenityOS shows that this same paradigm also works fine in C++. That includes something similar to the ? operator, though it's closer to a raw function call. SerenityOS is the first functional OS (as in "boots on actual hardware and has a GUI") I've seen that dares question the 1970s int main() using modern C++ constructs instead, and the API is simply a lot b…

Google has been doing a very similar, but definitely somewhat uglier, thing with StatusOr and Status (as seen in absl and protobuf) for quite some time. A long time ago, there was talk about a similar concept for C++ based on exception objects in a more "standard" way that could feasibly be added to the standard library, the expected class. And... in C++23, std::expected does exist[1], and you don't need to use excep…

I had a look. In classic C++ style, if you use *x to get the ‘expected’ value, when it’s an error object (you forgot to check first and return the error), it’s undefined behaviour!

Messing up error handling isn’t hard to do, so putting undefined behaviour here feels very dangerous to me, but it is the C++ way.

Re: Matt Godbolt sold me on Rust by showing me C++

#207

Earlier quoted context omitted.

> the `anyhow` crate is basically mandatory from an ergonomics standpoint in every Rust project I start If you use `anyhow`, then all you know is that the function may `Err`, but you do not know how - this is no better than calling a function that may `throw` any kind of `Throwable`. Not saying it's bad, it is just not that much different from the error handling in Kotlin or C#.

I know a ‘C’ code base that treats all socket errors the same and just retries for a limited time. However there are errors that make no sense to retry, like invalid socket or socket not connected. It is necessary to know what socket error occurred. I like how the Posix API defines an errno and documents the values. Of course this depends on accurate documentation.

This is an IDE/documentation problem in a lot of cases though. No one writes code badly intentionally, but we are time constrained - tracking down every type of error which can happen and what it means is time consuming and you're likely to get it wrong.

Whereas going with "I probably want to retry a few times" is guessing that most of your problems are the common case, but you're not entirely sure the platform you're on will emit non-commoncases with sane semantics.

Re: Matt Godbolt sold me on Rust by showing me C++

#208
post #146
post #76

Earlier quoted context omitted.

My opinion is that if you need to run extra tools/linters in order to catch basic errors, the language & its compiler are not doing enough to protect me from correctness bugs. I do run clippy on my Rust projects, but that's a matter of style and readability, not correctness (for the most part!).

There's a bit more nuance here than 'basic errors', and modern c compilers offer a lot of options _if you need to use them_. I appreciate that there are guardrails in a tool like rust, I also appreciate that sharp tools like c exist, they both have advantages.

To be clear, the only difference between Rust and C here is whether the conversion happens by default or not. Rust doesn't do the conversion by default but will let you do it if you want to, with `as`.

There are also more type-safe conversion methods that perform a more focused conversion. Eg a widening conversion from i8 -> i16 can be done with .into(), a narrowing conversion from i16 -> i8 can be done with .try_into() (which returns a Result and forces you to handle the overflow case), a signed to unsigned reinterpretation like i64 -> u64 can be done with .cast_unsigned(), and so on. Unlike `as` these have the advantage that they stop compiling if the original value changes type; eg if you refactor something and the i8 in the first example becomes an i32, the i32 -> i16 conversion is no longer a widening conversion so the `.into()` will fail to compile.

Re: Matt Godbolt sold me on Rust by showing me C++

#209
post #89

Earlier quoted context omitted.

Google has been doing a very similar, but definitely somewhat uglier, thing with StatusOr and Status (as seen in absl and protobuf) for quite some time. A long time ago, there was talk about a similar concept for C++ based on exception objects in a more "standard" way that could feasibly be added to the standard library, the expected class. And... in C++23, std::expected does exist[1], and you don't need to use excep…

I had a look. In classic C++ style, if you use *x to get the ‘expected’ value, when it’s an error object (you forgot to check first and return the error), it’s undefined behaviour! Messing up error handling isn’t hard to do, so putting undefined behaviour here feels very dangerous to me, but it is the C++ way.

The reason it works this way is there's legitimately no easy way around it. You're not guaranteed a reasonable zero value for any type, so you can't do the slightly better Go thing (defined behavior but still wrong... Not great.) and you certainly can't do the Rust thing, because... There's no pattern matching. You can't conditionally enter a branch based on the presence of a value.

There really is no reasonable workaround here, the language needs to be amended to make this safe and ergonomic. They tried to be cheeky with some of the other APIs, like std::variant, but really the best you can do is chuck the conditional branch into a lambda (or other function-based implementation of visitors) and the ergonomics of that are pretty unimpressive.

Edit: but maybe fortune will change in the future, for anyone who still cares:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p26...

Re: Matt Godbolt sold me on Rust by showing me C++

#210
post #48

Earlier quoted context omitted.

> The one thing that sold me on Rust (going from C++) was that there is a single way errors are propagated: the Result type. No need to bother with exceptions This isn't really true since Rust has panics. It would be nice to have out-of-the-box support for a "no panics" subset of Rust, which would also make it easier to properly support linear (no auto-drop) types.

`panic` isn’t really an error that you have to (or can) handle, it’s for unrecoverable errors. Sort of like C++ assertions. Also there is the no_panic crate, which uses macros to require the compiler to prove that a given function cannot panic.

You can handle panics. It’s for unrecoverable errors, but internally it does stack unwinding by default like exceptions in C++.

You see this whenever you use cargo test. If a single test panics, it doesn’t abort the whole program. The panic is “caught”. It still runs all the other tests and reports the failure.

Post reply on HN