Live data from Hacker News

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

github.com

131–140 of 174 posts

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

#131

Earlier quoted context omitted.

std::optional is a poor shadow of Option. It's what happens when C++ programmers who've seen a Maybe type in a window (years ago by the way, this isn't inspired by Rust, it was just stuck in the standardization process until C++ 17) but are starved of proper types and basic features like pattern matching try to imitate what they saw. As a result for example std::optional doesn't exist, because to a C++ programmer it…

std optional is based on boost optional which was written in 2003 before any sort of lambdas made monadic operations usable. The main concern with that component was ensuring we can allocate stack storage for an object that may or may not be initialized. The reference is easily achievable by using T* so is of minimal value, but also poses some more semantic problems since a reference is not copyable while an optional…

I actually don't care that much about the monadic functions.

For me the important use case is pattern matching, which C++ doesn't yet have. Pattern matching really changes how you see the entire language.

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

#132
post #123

Earlier quoted context omitted.

Nim is stack allocated unless you specifically mark a type as a reference, and "does not use classical GC algorithms anymore but is based on destructors and move semantics": https://nim-lang.org/docs/destructors.html Where Rust won't compile when a lifetime can't be determined, IIRC Nim's static analysis will make a copy (and tell you), so it's more as a performance optimisation than for correctness. Regardless of th…

> Where Rust won't compile when a lifetime can't be determined, IIRC Nim's static analysis will make a copy (and tell you), so it's more as a performance optimisation than for correctness. Wait, how does that work? For example, take the following Rust function with insufficient lifetime specifiers: pub fn lt(x: &i32, y: &i32) -> &i32 { if x You're saying Nim will change one/all of those references to copies and will…

It will not emit warnings saying it did that. The static analysis is not very transparent. (If you can get the right incantation of flags working to do so and it works, let me know! The last time I did that it was quite bugged.)

Writing an equivalent program is a bit weird because: 1) Nim does not distinguish between owned and borrowed types in the parameters (except wrt. lent which is bugged and only for optimizations), 2) Nim copies all structures smaller than $THRESHOLD regardless (the threshold is only slightly larger than a pointer but definitely includes all integer types - it's somewhere in the manual) and 3) similarly, not having a way to explicitly return borrows cuts out much of the complexity of lifetimes regardless, since it'll just fall back on reference counting. The TL;DR here though is no, unless I'm mistaken, Nim will fall back on reference counting here (were points 1 and 2 changed).

For clarity as to Nim's memory model: it can be thought of as ownership-optimized reference counting. It's basically the same model as Koka (a research language from Microsoft). If you want to learn more about it, because it is very neat and an exceptionally good tradeoff between performance/ease of use/determinism IMO, I would suggest reading the papers on Perseus as the Nim implementation is not very well-documented. (IIRC the main difference between Koka and Nim's implementation is that Nim frees at the end of scope while Koka frees at the point of last use.)

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

#133

Earlier quoted context omitted.

std optional is based on boost optional which was written in 2003 before any sort of lambdas made monadic operations usable. The main concern with that component was ensuring we can allocate stack storage for an object that may or may not be initialized. The reference is easily achievable by using T* so is of minimal value, but also poses some more semantic problems since a reference is not copyable while an optional…

I actually don't care that much about the monadic functions. For me the important use case is pattern matching, which C++ doesn't yet have. Pattern matching really changes how you see the entire language.

C++ has pattern matching through overloading.

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

#134
post #122
post #118

Earlier quoted context omitted.

You say: > Rust `Arc` = C++ `std::shared_ptr` GP says: > Rust requires shared pointers (Arc) to also explicitly implement some sort of Mutex-equivalent runtime safety check in order to mutate the data. Which is it? > An example of a big C++ codebase using something similar is Chromium ... Chromium's smart pointers are similar to their standard counterparts -- no mutexes for write access to pointed data. Also, tangent…

Both are true, Rust just has more restrictions. It’s not completely equivalent, but you can think of `Arc ` as `std::shared_ptr ` as in if you use `unsafe` or `const_cast` you can bypass mutability restrictions. Otherwise to mutate you need another abstraction doing `unsafe` things for you, such as `Mutex`. I mentioned Chromium because they also differentiate between thread safe and non-thread safe shared pointers. I…

Perhaps I am not making myself clear here:

- RefCounted: It's like shared_ptr but refcount load/modify/store operation is not atomic, thus not thread-safe. No synchronization for pointed data.

- RefCountedThreadSafe: It's like shared_ptr. This means refcount load/modify/store is atomic, so has overhead, yet safe to pass across thread boundaries. Again, just like shared_ptr, no synchronization for pointed data.

- Locker class above: It's an (incomplete) wrapper around shared_ptr where read-only access goes through a shared lock and rw access goes through an exclusive lock. I suppose this is what rust's ARC guarantees at compile-time with less overhead the sketch above?

So;

> Both are true, Rust just has more restrictions.

No, both are not true, my understanding of ARC ~= Locker && ARC > shared_ptr

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

#135
post #134
post #122

Earlier quoted context omitted.

Both are true, Rust just has more restrictions. It’s not completely equivalent, but you can think of `Arc ` as `std::shared_ptr ` as in if you use `unsafe` or `const_cast` you can bypass mutability restrictions. Otherwise to mutate you need another abstraction doing `unsafe` things for you, such as `Mutex`. I mentioned Chromium because they also differentiate between thread safe and non-thread safe shared pointers. I…

Perhaps I am not making myself clear here: - RefCounted: It's like shared_ptr but refcount load/modify/store operation is not atomic, thus not thread-safe. No synchronization for pointed data. - RefCountedThreadSafe: It's like shared_ptr. This means refcount load/modify/store is atomic, so has overhead, yet safe to pass across thread boundaries. Again, just like shared_ptr, no synchronization for pointed data. - Lock…

I think that's where you're confused: `Arc` does not do any synchronization, again it's pretty much the same as `std::shared_ptr` (hence the name Arc: Atomically Reference Counted).

Your `Locker` does not do what `Arc` does, even at compile time, because it does not allow concurrent access, like an `Arc` would. Your `Locker` is more like an `Arc>`.

Best equivalent you can get in C++ is `Arc` = `std::shared_ptr`.

https://doc.rust-lang.org/std/sync/struct.Arc.html

> Shared references in Rust disallow mutation by default, and Arc is no exception: you cannot generally obtain a mutable reference to something inside an Arc. If you need to mutate through an Arc, use Mutex, RwLock, or one of the Atomic types.

I guess you could get the final pieces to get something similar by creating `Send` and `Sync` traits in C++: https://doc.rust-lang.org/nomicon/send-and-sync.html. I think the main pain point here is that you cannot auto-derive `Send` and `Sync` so it would end up being very verbose.

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

#136
post #104

Earlier quoted context omitted.

Rust `Box` = C++ `std::unique_ptr`, both have the same ABI (just pointers) Rust `Arc` = C++ `std::shared_ptr` Rust `Rc` = C++ `std::shared_ptr` but using a simple integer instead of an atomic so it is not thread safe `Arc` and `Rc` do not allow you to mutate their contents directly so instead you should use "interior mutability" using something like a `Mutex` (thread-safe) or `RefCell` (not thread-safe), which have r…

> both have the same ABI (just pointers) This is not actually true, but it's close enough for your purposes here. But just to be clear about it, see stuff like this: https://stackoverflow.com/questions/58339165/why-can-a-t-be-...

Another reason it is not true: Rust has fat pointers, eg. `std::unique_ptr` and `Box` both contain the same allocation data, but `Box` will be 128-bit on 64-bit systems.

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

#137
post #25

Earlier quoted context omitted.

You're describing what it would do in a sane world where WG21 cared about safety. In this world, as the document you've linked says: "The behavior is undefined if *this does not contain a value." The operators for such access are actually `noexcept` - the exception you're apparently relying on would be illegal.

Can we salvage this by forbidding * on optional with compiler warnings (as errors)?

clang-tidy has a check for this -- it's not a compiler check but with clangd and LSP, almost every code editor can show an inline warning: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unch...

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

#138
post #39

Earlier quoted context omitted.

Step 1 of API design: Always make the easiest and shortest way the wrong way.

It sucks but it's easy to review and avoid, probably could be checked statically by linters too.

Indeed: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unch...

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

#139

Earlier quoted context omitted.

My point was that overhead is one common objection to C++'s shared/unique pointers - everything is a method call - but that could be mitigated by the compiler itself doing the type of raw-pointer lowering, when safe, that the get() method permits. From other replies in this thread is seems that Rust's borrow-checker addresses the high level issues of object ownership and thread safety - it's not just a replacement fo…

The main overhead of using shared/unique ptr for everything where you could have used stack allocation is not the extra method call for get etc, it’s the extra heap allocation. Compilers can probably inline get, but they can’t change heap allocations to stack allocations in general.

If you're declaring an object on the stack, then there is no reason to be using a pointer to refer to it. You could take the address of it and assign that to a raw pointer if you wanted to for some (perverse!) reason, but you'd never then assign that to a shared/unique_ptr since that implies ownership.

T t1; // stack, reference as t1

T* t2 = new T(); // heap, raw pointer, reference as * t2

std::unique_ptr t3 = std::make_unique(); // heap, smart pointer, reference as * t3

T* pt = &t1; // Create a raw pointer to t1! Bad idea!

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

#140

Earlier quoted context omitted.

My point was that overhead is one common objection to C++'s shared/unique pointers - everything is a method call - but that could be mitigated by the compiler itself doing the type of raw-pointer lowering, when safe, that the get() method permits. From other replies in this thread is seems that Rust's borrow-checker addresses the high level issues of object ownership and thread safety - it's not just a replacement fo…

borrowck is a semantic check. So, it's not a replacement for some particular C++ feature per se, it's not a feature in the sense you mean at all, it's just that while C++ and Rust both have these same semantic rules in place, Rust checks them and C++ does not. When you as a programmer inevitably get something wrong and break the rules, in Rust your program won't compile, in C++ it just has some arbitrary misbehaviour…

That last paragraph destroys your whole argument.

If you really believe that Google and FaceBook (etc, etc) hire morons who don't care if their code works, then you are not qualified to talk about programming languages.

Post reply on HN