Live data from Hacker News

Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

github.com

81–90 of 174 posts

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#81
post #47

Earlier quoted context omitted.

The people criticizing std::optional are doing a very poor job. Here's the big issue: unchecked access to std::optional with operator* has undefined behavior when there's no value. This is unforgivably bad design since you can enforce exhaustive checking at compile time, but C++ isn't going in that direction. std::optional offers value() for checked access too, but that checks at runtime and throws an exception. It i…

can you show me how rust does this? I'm genuinely curious. I've made a toy example to show how c++ checks for undefined behavior at compile time, I am unaware of rust being able to do the same without runtime costs (however small they may be, this is a toy example after all) https://godbolt.org/z/cT9bqz8z7

[deleted]

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#82
post #47

Earlier quoted context omitted.

The people criticizing std::optional are doing a very poor job. Here's the big issue: unchecked access to std::optional with operator* has undefined behavior when there's no value. This is unforgivably bad design since you can enforce exhaustive checking at compile time, but C++ isn't going in that direction. std::optional offers value() for checked access too, but that checks at runtime and throws an exception. It i…

can you show me how rust does this? I'm genuinely curious. I've made a toy example to show how c++ checks for undefined behavior at compile time, I am unaware of rust being able to do the same without runtime costs (however small they may be, this is a toy example after all) https://godbolt.org/z/cT9bqz8z7

The point is that Option in Rust doesn't have undefined behavior in any case, even if the values aren't known at compile time. Exhaustiveness is always checked at compile time, unlike C++ where operator* offers an escape hatch where nothing is checked in non-constexpr contexts.

"Make everything constexpr" isn't a real solution to UB, in the same way that "make all functions pure" isn't a solution for managing side effects.

Not adding UB to your APIs, on the other hand, is a real solution.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#83
post #73

Earlier quoted context omitted.

pub fn expect(self, msg: &str) -> T So that says it's a method (its first parameter is the type itself, but named self rather than as a normal parameter so we can use method syntax instead of calling the function Option::expect) but it also takes an immutable reference to a string slice. That second parameter, msg, is the text for a diagnostic if/ when you're wrong. So, in a sense it's like value() but the diagnostic…

Right, but that's redundant with the stack trace. It's not actually helpful to run a big program I don't know very well and panic with a single "your goose isn't cromulent!" message from a call 20 levels deep. In your example, it's likely that the person who sees this message won't have enough context to understand it; it's more like a debugging assert. Since you'll need a debugger and a breakpoint anyway, the messag…

Maybe it's just me but a note from the developer stating why it's important that some particular value be present is exactly the sort of help I would like when looking at a call stack that's dozens of levels deep. Especially considering that a panic terminates execution - I very much would like to know what was so critical that the program had to preemptively crash up front and not after pawing through code and docs.

I think it's pretty odd to use a quick example someone rattled off on a web forum to explain a function's behaviour as evidence of its usefulness or lack thereof, as if the only thing a person could possibly write in a freeform error message is "Our goose finder should always find a goose".

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#84
post #75

Can someone familiar with both please explain the benefit of Rust's borrow checker memory management model over C++'s std::unique_ptr and shared_ptr ? Is there some safety argument to prefer Rust's model, or is it something else ? I'm not aware of any C++ compiler doing it, but it seems smart pointer overhead could be automatically and safely reduced (in same way one can do it manually) by the compiler lowering the g…

The C++ smart pointers dont prevent multiple threads from mutating the pointed-to data at the same time; multiple threads can both access a unique_ptr at the same time and mutate its contents. Rust requires shared pointers (Arc) to also explicitly implement some sort of Mutex-equivalent runtime safety check in order to mutate the data. Rust also has explicit notion of thread ownership, and whether individual types ar…

So ARC is something like the following?

    template 
    struct Locker {
        using M = std::shared_mutex;
        struct Locked {
            Locked(mtx, value) : m_lock(mtx), m_value(value) {}
            // operator->, operator*, get, etc.
        private:
            std::lock_guard m_lock;
            std::shared_ptr m_value;
        };

        struct Shared {
            Shared(mtx, value) : m_lock(mtx), m_value(value) {}
            // operator->, operator*, get, etc.
        private:
            std::shared_lock m_lock;
            std::shared_ptr m_value;
        };
    
        Shared shared() { return Shared{m_mutex, m_value}; }
        Locked locked() { return Locked{m_mutex, m_value}; }

        // a nice forwarding ctor that prevents null m_value

    private:
        std::shared_ptr m_value;
        M m_mutex;
    };

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#85
post #75

Can someone familiar with both please explain the benefit of Rust's borrow checker memory management model over C++'s std::unique_ptr and shared_ptr ? Is there some safety argument to prefer Rust's model, or is it something else ? I'm not aware of any C++ compiler doing it, but it seems smart pointer overhead could be automatically and safely reduced (in same way one can do it manually) by the compiler lowering the g…

The C++ smart pointers dont prevent multiple threads from mutating the pointed-to data at the same time; multiple threads can both access a unique_ptr at the same time and mutate its contents. Rust requires shared pointers (Arc) to also explicitly implement some sort of Mutex-equivalent runtime safety check in order to mutate the data. Rust also has explicit notion of thread ownership, and whether individual types ar…

Thanks.

So basically Rust is combining object ownership and thread safety while C++ keeps thread safety separate, which would seem to provide more flexibility, but also lets you shoot yourself in the foot.

Just thinking out loud, I wonder if C++ could better address this by also having a class of thread-aware smart pointers? -- but the problem is that C++ always has the old/new (C, C++) way of doing things - pthreads vs std::thread, std::mutex, etc, so even if the language provides easier ways of writing bug free code, there is no way to force developers to use those facilities.

In C++ there is also the issue of how to make statically allocated data structures thread safe in an enforceable way. Another kind of smart reference object, perhaps? Disallow global objects not accessed by such references?

C++ (which I have used since long before C++11) really wants to be two conflicting things - encompassing C's low level role as the ultimate systems programming language with no guardrails, while also wanting to compete as a much higher-level safer language for application developers. Perhaps the two safe+unsafe roles can be better combined into one language if one were to start from scratch. I'm not sure that Rust gets it right either - erring in the other direction by not being flexible enough.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#86
post #82

Earlier quoted context omitted.

can you show me how rust does this? I'm genuinely curious. I've made a toy example to show how c++ checks for undefined behavior at compile time, I am unaware of rust being able to do the same without runtime costs (however small they may be, this is a toy example after all) https://godbolt.org/z/cT9bqz8z7

The point is that Option in Rust doesn't have undefined behavior in any case, even if the values aren't known at compile time. Exhaustiveness is always checked at compile time, unlike C++ where operator* offers an escape hatch where nothing is checked in non-constexpr contexts. "Make everything constexpr" isn't a real solution to UB, in the same way that "make all functions pure" isn't a solution for managing side ef…

You can actually implement the C++ behavior, if you want:

    unsafe fn super_unwrap(x: Option) -> T {
        match x {
            Some(val) => val,
            None => unreachable_unchecked!(),
        }
    }

But defaults matter, and Rust certainly doesn’t make this kind of thing ergonomic (which is a correct decision on the Rust designers’ part).

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#87
post #73

Earlier quoted context omitted.

Right, but that's redundant with the stack trace. It's not actually helpful to run a big program I don't know very well and panic with a single "your goose isn't cromulent!" message from a call 20 levels deep. In your example, it's likely that the person who sees this message won't have enough context to understand it; it's more like a debugging assert. Since you'll need a debugger and a breakpoint anyway, the messag…

Maybe it's just me but a note from the developer stating why it's important that some particular value be present is exactly the sort of help I would like when looking at a call stack that's dozens of levels deep. Especially considering that a panic terminates execution - I very much would like to know what was so critical that the program had to preemptively crash up front and not after pawing through code and docs.…

I see your point, but my experience is that you need the stack trace first, and the developer’s explanation second. Asserts crashing with a message that makes perfect sense in its context but is completely useless for debugging are the bane of my workweek.

Now I appreciate a clear explanation for an uncommon assert and for example, OpenCV could do with more of those, but in most functions, seeing the line that throws the error is enough to understand.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#88
post #62

Earlier quoted context omitted.

Compile time checked pattern matching: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html

That matches the 'static_assert' portion of my sample code. The implied claim of the parent I replied to was that rust could do this even for runtime values, such as the one I am using in the main of my sample. In c++ it is the same function running both the compile time check and the unchecked runtime variant, so there is zero overhead at runtime. I can't possibly think of a way how rust would be able to make the sa…

You’re correct. Rust can’t statically prove which enum variant is inhabited. You do need a runtime switch, the difference is (at least in safe code) it statically forces you to indeed do that runtime switch.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#89
post #37

Earlier quoted context omitted.

> The practical use of optional is that you know where T is going to be constructed and can reason about the memory layout. The practical use for me is making interfaces safer. Where I saw colleagues use pointers as optionals, end up mis-tracking what can be null and what can't, only checking it inconsistently, and triggering UB, I now have a clear distinction between optional and non-optional arguments/returns with…

> Most of the time, I want to pass/return a reference Surprised to hear that you want to return a reference so frequently.

How else would you implement C++’s vector::operator[] for example?

This to me is the clearest example of something that’s safe in Rust, and impossible to make safe in C++.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#90

Earlier quoted context omitted.

> Most of the time, I want to pass/return a reference Surprised to hear that you want to return a reference so frequently.

These kinds of discussions remind me that not everyone codes in the same domain where the same patterns dominate. I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments and instead have the imagination that others may code in different domains where different patterns dominate. Me? References get returned all the time because you want to access some state store's vec…

> I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments

This is an uncharitable characterisation of what I said.

> References get returned all the time because you want to access some state store's vector of things without copying the vector just to ask "are any of the elements X?"

This is what const references are for. Returning an optional&> to query if it contains an element would not be appropriate.

Post reply on HN