Live data from Hacker News

Safety: A comparaison between Rust, C++ and Go

nested.substack.com

111–120 of 188 posts

Re: Safety: A comparaison between Rust, C++ and Go

#111

Earlier quoted context omitted.

Oh I think we probably see largely eye-to-eye. My weapon of choice when there are no other constraints is Haskell, and one reason I really like Rust is that I can get a lot of the Haskell features I like in a highly-performant setting. Most of the C++ I maintain these days is in whole or in part generated by Haskell. And if I have to write something fast by hand and it doesn't need to link to stuff I need, I reach fo…

> Most of the C++ I maintain these days is in whole or in part generated by Haskell Can you expand on that? I'm currently researching something similar but lower level & lisp instead of Haskell. It would help to see some existing examples to figure out if it's worth it or not.

Ideally we'll just be able to open source it soon!

Basically we have a nice Haskell DSL for generating arbitrary C++, and we deal with lots of code you wouldn't want to write by hand (big nested switch statements and other kinds of state-machine logic, choices about loop unrolling, lots of template overloads, SIMD intrinsics that require immediate values, etc. etc.) so we write Haskell that generates C++ and feeds it to e.g. `clang`.

Some of this is directly in Haskell, and some of it is little compilers mostly done using Megaparsec. It's a really nice approach where it fits!

Re: Safety: A comparaison between Rust, C++ and Go

#112

This would be a better article if the UB in the C++ example wasn't blindingly obvious-- no C++ programmer worth a damn would ever write this.

Yea and C++ static analysis tools already warn for the case, so even for new C++ programmers where it might not be entirely obvious, its still easy to catch the error.

Re: Safety: A comparaison between Rust, C++ and Go

#113
post #91

Earlier quoted context omitted.

I think you meant thanks to a sane(r) macro system? Both Rust and C++ use monomorphisation for generics, I believe shitty compiler errors are due to C++'s templating.

I’m what way can you have ‘generics’ in C++ that are not based on templating? I am almost certain that any implementation of anything ‘generic’ templates are inherently involved. Maybe I’m wrong about what you mean by generics though.

There are concepts now which are close enough to Rust's traits.

Re: Safety: A comparaison between Rust, C++ and Go

#114

Earlier quoted context omitted.

The fact that you may know that make_appender definition being bad does not mean every C++ user knows as well. It's also impossible for you to know ALL the possible bad, UB leading C++ code out there. I think the point the author tries to make is that, while C++ and Rust are probably "the same" for the most skillful and disciplined programmers (such as you), for average human, Rust just catches way more errors they m…

Not to disagree necessarily, but if you (give me a little rope) bucket languages that are hard to get past the compiler but more often correct when you do (Rust, Haskell), and languages where it's pretty easy to get something past the compiler and tweak it until it works well enough for your purposes (JS, C/C++), the tweak-it-until-it-kinda-works languages are fucking killing it on adoption.

JS and C++ are killing it on adoption because they had near monopolies for an extended period of time in their respective areas (browser, native higher level language). They are popular despite their obvious (some in hindsight) shortcomings due to lack of alternatives in the same categories.

Re: Safety: A comparaison between Rust, C++ and Go

#115
The author could compile c++ with the sanitizers, i.e. -fsanitize=address,undefined and make a make_appender function that leverages perfect forwarding...:

  template
  auto make_appender(S&& suffix)
  {
    return [perf_fwd_suffix = std::tuple{std::forward(suffix)}](std::vector&& items)
    {
        return append(std::move(items), std::get(perf_fwd_suffix));
    };
  }
see: https://godbolt.org/z/M9P4MK4a8

Re: Safety: A comparaison between Rust, C++ and Go

#116
post #70

Earlier quoted context omitted.

I dunno man. I've done C, C++, JavaScript and TypeScript professionally for significant chunks of my career, and the trend that I've observed has overwhelmingly been towards stricter compilers. For example in the front-end world, TypeScript has absolutely exploded in adoption. Everyone could be still using JavaScript, but companies from startups to huge corporates have explicitly decided they want compile type safety…

Oh I think we probably see largely eye-to-eye. My weapon of choice when there are no other constraints is Haskell, and one reason I really like Rust is that I can get a lot of the Haskell features I like in a highly-performant setting. Most of the C++ I maintain these days is in whole or in part generated by Haskell. And if I have to write something fast by hand and it doesn't need to link to stuff I need, I reach fo…

Ha, Rust community is very energetic, but IMHO they largely put that energy to good use!

I’m using it for a new project and honestly I’m using it more for the modern tooling and easy C interop than the safety features, but I’m a fan overall. Think it’s a really good language.

Re: Safety: A comparaison between Rust, C++ and Go

#117

Earlier quoted context omitted.

You can add [[clang::lifetimebound]] on the suffix parameter and you get :21:35: warning: temporary whose address is used as value of local variable 'append34' will be destroyed at the end of the full-expression [-Wdangling] auto append34 = make_appender({3, 4});

You dont need annotations, cppcheck already warns with: test.cpp:16:45: error: Using object that is a temporary. [danglingTemporaryLifetime] assert((std::vector {1, 2, 3, 4} == append34({1, 2}))); // FAIL: UB ^ test.cpp:3:12: note: Return lambda. return [&](std::vector && items) { ^ test.cpp:2:50: note: Passed to reference. auto make_appender(std::vector const& suffix) { ^ test.cpp:4:36: note: Lambda captures variabl…

As awesome as that is, cppcheck doesn't seem ready for real-world use. Literally the first invocation I ran resulted in this error:

  error: Syntax Error: AST broken, binary operator '!=' doesn't have two operands. [internalAstError]
   explicit operator bool() const { return this->get() != pointer(); }
This is for a simple wrapper class that looks like this:

  template
  class Foo : private Bar {
  public:
   typedef value_type *pointer;
   pointer get() const { return ...; }
   explicit operator bool() const { return this->get() != pointer(); }
  };
Another example:

  void foo(uintptr_t const (&input)[2]) {
   if constexpr (sizeof(uintptr_t) == sizeof(int) && sizeof(long long) == 2 * sizeof(int)) {
    long long value;
    memcpy(&reinterpret_cast(&value)[0], &input[0], sizeof(input[0]));
    memcpy(&reinterpret_cast(&value)[1], &input[1], sizeof(input[1]));
   }
  }

  error: The address of local variable 'value' is accessed at non-zero index. [objectIndex]
    memcpy(&reinterpret_cast(&value)[1], &input[1], sizeof(input[1]));
                                                 ^

Re: Safety: A comparaison between Rust, C++ and Go

#118
post #9

Earlier quoted context omitted.

> Unless clang-tidy has false positives, in which case the comparison isn't apples to apples then. Confused... so you're suggesting Rust's checks are somehow free of false positives? Doesn't the halting problem get in the way? One Rust-specific example: https://www.reddit.com/r/rust/comments/nr7a33/is_the_borrow_...

I find it so weird that the Rust community is borderline evangelical about memory safety when a) it's not actually memory safe once you start doing heavy shit b) modern C++ is quite memory safe and c) there are so many other great reasons to like Rust. Memory safety in serious systems software is something that you approach asymptotically and/or probabilistically. Rust makes it easier to be memory safe in a lot of sc…

> a) it's not actually memory safe once you start doing heavy shit b) modern C++ is quite memory safe

This just doesn't capture the problem that memory safety solves. A crashed program is not the worst-case scenario that it's trying to avoid. Even the most memory-safe language supports exiting early with an error message, or whatever.

In terms of language semantics, there is an all-or-nothing line between memory safety and undefined behavior. A memory safe program does what it says, locally, step-by-step, according to the semantics of the language. When a program exhibits UB, those guarantees are lost.

Of course, as you note, unsafe Rust also lets you violate memory safety, and in fact any memory safe language is at the mercy of its implementation and host. The reason people get evangelical about Rust's memory safety is one level higher: it offers a bridge back to memory safety, such that unsafe code stands on the same footing as the core language. When either are bug-free, the compiler can ensure they are used correctly, using the same type system features for both.

Modern C++ is certainly much less error-prone than the bad old days of manual `new` and `delete`, but it doesn't have an answer to this "unsafe encapsulation." To the contrary, modern C++ actually adds a bunch of new ways to violate memory safety by misusing library APIs. Iterator invalidation, use-after-move, string_view and span and borrowed ranges, by-reference lambda and coroutine captures, etc.

This all means that "serious systems software" in C++ has to approach memory safety via defensive copying or refcounting, copious use of sanitizers, and sandboxed sub-processes. Meanwhile, Rust programs can do things that would be unthinkable in a large C++ codebase, because the assumptions of both the language and unsafe code are encoded in the type system. (For example: https://manishearth.github.io/blog/2015/05/03/where-rust-rea...) It's a qualitatively different solution to the problem.

Re: Safety: A comparaison between Rust, C++ and Go

#119

Rust has a lot of great qualities that C++ lacks, but comparing `rustc` to `gcc` or `clang` on move-semantics checking is just kind of silly these days. `rustc` has `clang-tidy` built in. `clang-tidy` is not letting you mutate or even access that moved-from "suffix" object without throwing an error. It's annoying that you need `clang-tidy` and ASAN and shit to get comparable runtime safety even in greenfield C++, but…

Objectively speaking, Rust does not "have a lot of great qualities that C++ lacks". Any new feature or improvement has its pluses, but also its minuses.

* Rust has traits, but does not support OOP. Architectures where OOP is particularly effective are proving to be a significant challenge for Rust - GUIs are the obvious one, but also game development Rust projects have to invent new approaches.

* Option/Result make the code flow obvious, but having to return them in nearly all function calls is tedious. Rust has had several attempts at alleviating this problem, but still hasn't matched the convenience of exceptions.

* match is IMO syntactic sugar and its benefits for correctness are being oversold. The fact that one is basically obliged to use it leads to code that's sometimes too deep. Exceptions would cut through this error-handling noise, if they were available.

* The build story is convenient, but this had the unintended effect of encouraging dependency explosion. Adding typical crates results in dozens of transitive dependencies being included in a project.

The notable improvement that Rust brings to the table is machine-verified memory safety with C++-like performance, but that of course comes at the cost of having to adapt code to what the borrow checker understands. If one needs the feature, then the cost is worth paying, if not, Rust is more of a personal choice than an inevitable conclusion.

And finally, perhaps Rust's biggest sin is that it's big, it's complex and there's no end in sight to the complexity spiral, just like for C++. I can only imagine the chagrin of the Rust community as they see themselves competing with Go for many projects where performance is not absolutely critical and often being second choice exactly because of this complexity.

Re: Safety: A comparaison between Rust, C++ and Go

#120

This would be a better article if the UB in the C++ example wasn't blindingly obvious-- no C++ programmer worth a damn would ever write this.

Yeah, agreed that a competent C++ dev would not write the code in the example. The charitable interpretation though is that errors of the same type can crop in real codebases, the example is just simplified for the purposes of discussion.
Post reply on HN