Live data from Hacker News

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

nested.substack.com

41–50 of 188 posts

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

#41
post #25

Earlier quoted context omitted.

A function returning a value that depends on the lifetime of the function's parameter is not crazy at all. Every class getter method that returns a reference to a member of the class does this.

Sure. But returning the address of a stack-allocated object is (usually) broken. There isn't a right and wrong here: sometimes you want to opt in to the check for that, sometimes you want to opt out of it. Sometimes I actually do want to fuck with addresses on the stack in weird, potentially architecture-dependent ways, it's rare but it happens. I happen to think that Rust's linear/affine typing is by far the most us…

> returning the address of a stack-allocated object is (usually) broken.

What matters is the lifetime, where the object lives is a rule of thumb for guessing lifetime that results from C++ trauma.

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

#42
post #40

Earlier quoted context omitted.

Rust isn't a startup business or (one hopes) a religion, or an MLM, or a home for sale. It's a useful tool among many. Why do people say things like: "It's better not to dilute the message"? Better for who? That's sales/marketing language, not engineering language. "The message"? Pardon my Francais, but WTF?

>Why do people say things like: "It's better not to dilute the message"? >Better for who? Better for everyone. When talking about a new thing, it would be really silly to emphasize how nice the logo is, how nice the package it comes in is, look at the awesome tape the box is closed with etc. If I turn the product off it even turns off! Look at the nice rounded corners of the device! It even can do async! Just like Ja…

I mean "message" how you want, HN is hosted in a free country.

But N=1 for you: as a serious polyglot user of Rust who knows it well and uses it all the time: this shit is a huge turnoff. It's a programming language. On a long enough timeline all the motivated hackers will end up knowing many programming languages well, they all have pros and cons.

Trying to boil important engineering decisions down to a tweet so that we can stay "on message" comes off like something someone would do if they were selling books or training or consulting services attached to a technology, which a priori gives them an agenda other than giving good advice.

So to keep it short: help people pick the right tool for the job without an agenda.

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

#43
post #11

Disingenuous. The bad C++ code is in the very first line of the "make_appender" definition: capturing the closure's environment by reference is nonsense: It is equivalent to returning a reference to an argument. It is not, then, a closure at all. Using a correctly-defined make_appender would not, then, produce undefined behavior when you use it, with or without "move". What the author has done here was to take a too-…

std::vector suffix{3, 4}; auto append34 = make_appender(suffix); // Version 1: test will work auto append34 = make_appender({3, 4}); // Version 2: test will fail I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed…

You can also 'const' your std::vector when you declare it and it won't work. Any linter or having warnings turned on will catch these issues.

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

#44
post #11

Disingenuous. The bad C++ code is in the very first line of the "make_appender" definition: capturing the closure's environment by reference is nonsense: It is equivalent to returning a reference to an argument. It is not, then, a closure at all. Using a correctly-defined make_appender would not, then, produce undefined behavior when you use it, with or without "move". What the author has done here was to take a too-…

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 make for them early. An extreme analogy would be trying to climb Everest all by yourself vs with a professional guide.

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

#45
I feel like a better comparison would be to use std::span (C++20) to mimic Rust's slice. Otherwise you might be tricked into thinking that adding

    // hey don't pass in a temporary
    auto make_appender(std::vector&& suffix) = delete;
(which turns the provided code into a compiler error under both g++ and clang++) is adequate to prevent the immediate class of issues (namely, C++ allows const Foo& and Foo&& to bind to temporaries).

Meanwhile, it's really really easy to make std::span dangle:

    #include 
    #include 
    #include 
    #include 
    
    std::vector append(std::vector&& items, std::span suffix) {
      items.insert(items.end(), suffix.begin(), suffix.end());
      return items;
    }
    
    auto make_appender(std::span suffix) {
      return [=](std::vector&& items) {
        return append(std::move(items), suffix);
      };
    }
    
    auto make_appender34() {
      std::vector vec = {3, 4};
      return make_appender(vec);
    }
    
    int main() {
      auto append34 = make_appender34();
      assert((std::vector{1, 2, 3, 4} == append34({1, 2})));
    }

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

#46
post #11

Disingenuous. The bad C++ code is in the very first line of the "make_appender" definition: capturing the closure's environment by reference is nonsense: It is equivalent to returning a reference to an argument. It is not, then, a closure at all. Using a correctly-defined make_appender would not, then, produce undefined behavior when you use it, with or without "move". What the author has done here was to take a too-…

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.

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

#47

Earlier quoted context omitted.

std::vector suffix{3, 4}; auto append34 = make_appender(suffix); // Version 1: test will work auto append34 = make_appender({3, 4}); // Version 2: test will fail I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed…

You can also 'const' your std::vector when you declare it and it won't work. Any linter or having warnings turned on will catch these issues.

const doesn't make a difference in this case. It's about the passed-by-reference object being destroyed after the function returns. That is because the object was passed as a temporary.

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

#48

Earlier quoted context omitted.

std::vector suffix{3, 4}; auto append34 = make_appender(suffix); // Version 1: test will work auto append34 = make_appender({3, 4}); // Version 2: test will fail I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed…

Again, this isn't a right/wrong thing. Rust moves by default (and a lot of people find "=" a weird pun for that), C++ copies by default and has rvalue-references and an explicit `std::move`. If you want copy in your C++ lambda, you start it `[=](...) {...}`, if you want take a pointer in your C++ lambda, you start it `[&](...) {...}`, if you want something trickier you do trickier stuff. Rust opts you in to the nitpi…

> They are remarkably similar, just with different defaults.

I'm going to lead with this, because I think it's most important: Culturally there's a world of difference. Safety is a part of Rust's culture. "Culture eats strategy for breakfast".

Take sorting. In C++ the default sort is unstable, while in Rust the default sort is stable, that's just those defaults you mentioned (each has both kinds), although the choice speaks to culture. But look closer, in C++ the sort has undefined behaviour if your type isn't totally ordered. In Rust you can't sort the partially ordered types without saying how to order them fully. Still, in both languages we can write a custom order, so what happens then? In C++ if your custom order is nonsense you get... undefined behaviour. In Rust sorting won't necessarily work with a nonsense custom order but the behaviour remains well defined.

> Rust opts you in to the nitpicky static analyzer and you have to opt out with `unsafe`

Unsafe gives you a small number of dangerous "super powers" needed to write efficient low-level code, it does not opt out of the borrow checker's analysis, or indeed most other checks. This misconception makes me wonder how much of what you've written is conjecture rather than practical experience.

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

#49
post #11

Disingenuous. The bad C++ code is in the very first line of the "make_appender" definition: capturing the closure's environment by reference is nonsense: It is equivalent to returning a reference to an argument. It is not, then, a closure at all. Using a correctly-defined make_appender would not, then, produce undefined behavior when you use it, with or without "move". What the author has done here was to take a too-…

It's possible the author actually made this mistake, and because c++ is C++ they didn't realize why/how. Not a lot of programmers have a good handle on c++, maybe even most don't... Hence these other languages.

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

#50

Earlier quoted context omitted.

std::vector suffix{3, 4}; auto append34 = make_appender(suffix); // Version 1: test will work auto append34 = make_appender({3, 4}); // Version 2: test will fail I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed…

Again, this isn't a right/wrong thing. Rust moves by default (and a lot of people find "=" a weird pun for that), C++ copies by default and has rvalue-references and an explicit `std::move`. If you want copy in your C++ lambda, you start it `[=](...) {...}`, if you want take a pointer in your C++ lambda, you start it `[&](...) {...}`, if you want something trickier you do trickier stuff. Rust opts you in to the nitpi…

There lies the problem with C++ state of affairs, most defaults are wrong.

It doesn't help that those of us that care about enabling the right defaults are a tiny minority as per C++ surveys.

https://blog.jetbrains.com/clion/2021/07/cpp-ecosystem-in-20...

Post reply on HN