Live data from Hacker News

Modern C++ Won't Save Us (2019)

alexgaynor.net

261–266 of 266 posts

Re: Modern C++ Won't Save Us (2019)

#261

Earlier quoted context omitted.

The idiomatic way to solve that in rust is to re-bind to the same variable name. if let Some(foo) = foo { /* ... * / } That's possible because in Rust name shadowing let foo = grab_foo_bytes(); let foo = parse_foo_bytes(foo); makes the previous binding of the variable no longer namable and thus no longer accessible, but doesn't drop it (and trigger RAII destructors). Now someone will probably come in and say "oh no,…

> The idiomatic way to solve that in rust is to re-bind to the same variable name. OK, that's reasonable. Is the idiomatic way to use optionals to introduce a layer of nesting? I prefer keeping functions very "flat"-looking. It sounds like Rust's optionals will give people an excuse to create labyrinthine functions where I'm constantly scrolling around to remind myself of what level of nesting I'm at and whether I'm…

> I prefer keeping functions very "flat"-looking.

I definitely agree. One way I do that is by having an internal function that takes a valid value and a public function that does the validating/error handling.

That doesn't always make sense though. There's a few other idiomatic ways to avoid nesting. Since statements evaluate to values, you can write

    let foo = if let Some(foo) = foo {
        foo
    } else {
        // Something that either evaluates to the same type as foo or returns early
    }
That's so common there's a special operator for it, ?. It essentially either early returns the sad path or evaluates to the happy path.

    fn get_foo() -> Option;

    fn frob() -> Option {
        let foo = get_foo()?;
        let bar = convert_to_bar(foo);
        Some(bar)
    }
I prefer to use Result to model missing data like cases instead of Option because it composes better. So that might be

    fn get_foo() -> Option;

    fn frob() -> Result {
        let foo = get_foo().ok_or(BarNotFound)?;
        let bar = convert_to_bar(foo);
        Some(bar)
    }


    #[derive(Debug, thiserror::Error)]
    #[error("Bar not found")]
    struct BarNotFound;
That last bit uses a stdlib macro and a very commonly used external lib macro to save a few lines of repetitive typing.

Edit: Also ? doesn't special case Result and Option. You can make your own type conform to the interface (trait) it requires. That would probably be weird though.

Re: Modern C++ Won't Save Us (2019)

#262
post #27

Earlier quoted context omitted.

I'm not sure what you mean by "global analysis," but Rust's borrow checker doesn't do any analysis that I'd consider "global."

The point of the borrow checker is to replace the global property 'all memory has an owner' with the (approximate, conservative) equivalent 'all code passes the borrow checker'. By tracing ownership information through each component, and forbidding (or ignoring via `unsafe`) situations which cannot be traced in this way, the latter property can be decomposed and solved locally. In other languages, like C++, we may s…

In other languages, like C++, we may still want this global property, but we can't break it down into local reasoning.

That's exactly it. You want some set of machine-checkable constraints which add up to the desired global properties. Rust managed to do that. Attempts to fix this in C++ yield a set of slightly leaky constraints which sort of almost do that. Fixing this requires taking things out of the language, which is unpopular.

It's embarrassing that the code below still compiles with default gcc options, in either C or C++ mode. Yes, it's terrible C++. The compiler allows it.

    #include 
    #include  
    int main(int argc, char* argv[]) {
        char buf[20] = "\0";
        char* s = buf;
        for (int i = 0; i
(Even Microsoft has "strcat" deprecated by default.)

Re: Modern C++ Won't Save Us (2019)

#263
post #186
post #83

Earlier quoted context omitted.

I'd submit that the kind of code that's difficult to translate - that is, code where it's not clear where the responsibility for the lifecycle of a given piece of memory lies - is already a bug factory.

Not every program has such a well-defined life cycle that fits into Rust’s memory model. There was a great post on why the wayland library’s rust implementation was abandoned. There was basically no point of Rust’s memory model there over C.

In the worst case you're no worse off. As several replies there said, a weak pointer model would have worked fine for that scenario.

Re: Modern C++ Won't Save Us (2019)

#264
post #184
post #31

Earlier quoted context omitted.

This amounts to saying that we should accept codebases continuing to contain exploitable vulnerabilities indefinitely. Perhaps for codebases that have a finite expiry date that's tolerable, but for a codebase that's expected to be maintained indefinitely I don't see how it can possibly be worthwhile - a rewrite will be a one-time cost, whereas exploitation is an ongoing cost that will surely exceed the one-time cost…

A program can fail in exceedingly many ways. It is basically impossible to formally verify a program. It’s great to start a new project in a “safer” language, but porting to another language is a different thing. So for example, let’s take SQLite. It is written in C, but it has an insane amount of tests. Would it benefit anyone to rewrite it in Rust? It will definitely be much more buggy for a long time.

> So for example, let’s take SQLite. It is written in C, but it has an insane amount of tests. Would it benefit anyone to rewrite it in Rust? It will definitely be much more buggy for a long time.

I bet it wouldn't be, actually. In my experience porting between languages is much easier and safer than people tend to think. Meanwhile even with all their tests (which certainly have a maintenance cost) SQLite has been known to have memory safety bugs.

Re: Modern C++ Won't Save Us (2019)

#265
post #263
post #186

Earlier quoted context omitted.

Not every program has such a well-defined life cycle that fits into Rust’s memory model. There was a great post on why the wayland library’s rust implementation was abandoned. There was basically no point of Rust’s memory model there over C.

In the worst case you're no worse off. As several replies there said, a weak pointer model would have worked fine for that scenario.

For new code, absolutely. But rewrites, especially when the new language can’t necessarily give huge safety guarantees as in this case are very bug-prone.

Re: Modern C++ Won't Save Us (2019)

#266

Earlier quoted context omitted.

> The idiomatic way to solve that in rust is to re-bind to the same variable name. OK, that's reasonable. Is the idiomatic way to use optionals to introduce a layer of nesting? I prefer keeping functions very "flat"-looking. It sounds like Rust's optionals will give people an excuse to create labyrinthine functions where I'm constantly scrolling around to remind myself of what level of nesting I'm at and whether I'm…

> I prefer keeping functions very "flat"-looking. I definitely agree. One way I do that is by having an internal function that takes a valid value and a public function that does the validating/error handling. That doesn't always make sense though. There's a few other idiomatic ways to avoid nesting. Since statements evaluate to values, you can write let foo = if let Some(foo) = foo { foo } else { // Something that e…

Interesting. Thank you for sharing.
Post reply on HN