Live data from Hacker News

Making C++ safe without borrow checking, reference counting, or tracing GC

verdagon.dev

131–140 of 226 posts

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#131
post #91
post #88

Earlier quoted context omitted.

> In fact the pattern described in the article is a common pattern in Rust and I make use of it all the time; the library for making use of it is `slotmap`. Slotmap uses unsafe everywhere, it's a memory usage pattern not supported by the borrow checker. It's basically hand-implementing use-after-free and double-free checks, which is what the borrow checker is supposed to do. Is that really a common pattern in Rust?

> Slotmap uses unsafe everywhere, it's a memory usage pattern not supported by the borrow checker. Is disabling the borrow checker really a common pattern in Rust? Wrapping "unsafe" code in a safe interface is a common pattern in Rust, yes. There is absolutely nothing wrong with using "unsafe" so long as you are diligent about checking invariants, and keep it contained as much as possible. Obviously the standard libr…

> Obviously the standard library uses some "unsafe" as well, for instance.

Most beautifully, MaybeUninit::assume_init() -> T

This unsafe Rust method says "I promise that I actually did initialize this MaybeUninit, so give me the T".

In terms of the resulting program the machine is not going to do any work whatsoever, a MaybeUninit and a T are the same size, they're in the same place, your CPU doesn't care that this is a T not a MaybeUninit now.

But from a type safety point of view, there's all the difference in the world.

Even though it won't result in emitting any actual CPU instructions, MaybeUninit::assume_init has to be unsafe. Most of the rest of that API surface is not. Because that API call, the one which emitted no CPU instructions, is where you took responsibility for type correctness. If you were wrong, if you haven't initialized T properly, everything may be about to go spectacularly wrong and there's no-one else to blame but you.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#132

Earlier quoted context omitted.

missing data structure helper -- didn't you already just name-check that though, since that's basically RefCell .. or if you're willing to roll the dice... UnsafeCell (aka "trust me I know what I'm doing")?

What you essentially want for the user to not write any unsafe code is this kind of interface: trait Allocator { fn allocate (&'a self, init: T) -> Handle ; fn deallocate (&'a self, handle: Handle ); fn read (&self, handle: Handle ) -> impl Deref ; fn write (&self, handle: Handle ) -> impl DerefMut ; } &'a RefCell is pretty close to a definition of Handle , except that Rust provides no implementations of allocate and…

Can you clone a Handle? If so, how do you handle using a clone after freeing it? If clones are refcounted, how do you handle cycles?

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#134
post #100
post #93

Earlier quoted context omitted.

Eh? This is a wild take. How do you draw the conclusion the default implementation is inadequate?

Because something like slotmap has to use `unsafe` to get around the inadequacies of the borrow checker...

Author of slotmap here.

There is absolutely no need for unsafe in slotmap. I chose to use unsafe (wrapped in a safe API) to reduce memory usage using intrusive linked freelists. If done using safe Rust this would involve `enum`s that would take up extra space.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#135
post #56

Earlier quoted context omitted.

> it's taken 17 years to get to this point Yes and no. Rust went through quite a bit of changes early on, ro the point that it's not really that similar of a language, and 1.0 was released in May 2015. That's still quite a while (8 years), but IMO doesn't quite mean the same thing as a language that's been around for 17 years with a similar level of adoption. My impression (from the outside) is that Rust usage is sti…

While that is true about Rust, most new languages are gonna have the same thing. It'll be years before they get to 1.0. Look at Zig, just about every new language. So I don't think it is valid to discount the 1.0 days because all languages are gonna need awhile to get to the 1.0 day. It still took 17 years of time investment to get Rust to where it is today.

I discount the early days because I don't think most professionals would rely on a language pre-1.0 that advertises it will stabilize at 1.0, so regardless of whether it spends 6 months or 10 years pre-1.0, with regard to wider adoption you'll only be able to make limited inferences about what that period means.

For example, you say 17 years, but it was a side project for the first four of those, and was only publicly announced as Rust from 2010 on from what I can find (given there's no way my memory is that good), but the following two announcements back that up.[1][2] If it's not really public or being advertised, I'm not sure how that can count towards adoption over time. Additionally, if it's advertised but with the caveat that it's pre-release and just for playing with as a proof of concept, should that count towards the adoption timeline? Counting periods when people were specifically warded off in a project's lifetime also seems odd to me, but your assessment would also use that as an indicator of what it's achieved over time.

I wouldn't say a novel languished in obscurity for a decade just because the author mentioned they were working on it at some point, I would assess it from the point it was released as a complete work and presented to people as a finalized product they could read expecting a full story.

  1: https://news.ycombinator.com/item?id=1498233

  2: https://news.ycombinator.com/item?id=1498232

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#136
post #88

> Borrow checking is incompatible with some useful patterns and optimizations (described later on), and its infectious constraints can have trouble coexisting with non-borrow-checked code. Not that this isn't true, but the rest of the article introduces a system with a superset of those limitations, gradually decreasing over time but never becoming a subset. In fact the pattern described in the article is a common pa…

> In fact the pattern described in the article is a common pattern in Rust and I make use of it all the time; the library for making use of it is `slotmap`. Slotmap uses unsafe everywhere, it's a memory usage pattern not supported by the borrow checker. It's basically hand-implementing use-after-free and double-free checks, which is what the borrow checker is supposed to do. Is that really a common pattern in Rust?

> Slotmap uses unsafe everywhere, it's a memory usage pattern not supported by the borrow checker.

Author of slotmap here. This is patently false.

Yes, the slotmap crate uses a lot of unsafe to squeeze out maximum performance. But it is not 'a memory usage pattern not supported by the borrow checker'. You can absolutely write a crate with an API identical to slotmap without using unsafe.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#137

> Borrow checking is incompatible with some useful patterns and optimizations (described later on), and its infectious constraints can have trouble coexisting with non-borrow-checked code. Not that this isn't true, but the rest of the article introduces a system with a superset of those limitations, gradually decreasing over time but never becoming a subset. In fact the pattern described in the article is a common pa…

Later on, it adds generational references and constraint references to relax the restrictions. These are both more flexible than SlotMap because they don't require a new parameter to be passed in from the callers (and callers' callers etc), which can cause problems when an indirect caller's signature can't change (trait method override, public API, drop, etc.)

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#138
post #91

Earlier quoted context omitted.

> Slotmap uses unsafe everywhere, it's a memory usage pattern not supported by the borrow checker. Is disabling the borrow checker really a common pattern in Rust? Wrapping "unsafe" code in a safe interface is a common pattern in Rust, yes. There is absolutely nothing wrong with using "unsafe" so long as you are diligent about checking invariants, and keep it contained as much as possible. Obviously the standard libr…

If unsafe means “safe but the compiler cannot verify” then I guess just consider .cpp to mean “safe but the compiler cannot verify” and we have suddenly made C++ memory safe

There's a related idea in Haskell, usually considered a memory safe language. You can write a program in Haskell that directly mutates memory, or does IO operations, freely, anywhere in the code. This violates functional purity and the compiler cannot offer its usual promises; your program may very well segfault from a bug in such code. But sometimes you just have to, perhaps to implement an algorithm efficiently.

Still, it is discouraged; both culturally in the language community, and discouraged through the subtle prodding of the language itself (such as everything being typed "IO", or the slightly ominous "unsafe" in the "unsafePerformIO".) Very often, the amount of code that must truly live in IO can be reduced to a few dozen lines, if that. That code is crucial to get right -- it's where the actual sequence of computation and external effects are handled. Such isolation allows the rest of the code to not have to worry about those matters.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#139

RIP all the modern languages that haven't made any improvements in memory management at all. There is so much low hanging fruit in programming language design and nobody is picking it up and instead everyone produces marginal improvements over existing languages.

> There is so much low hanging fruit in programming language design and nobody is picking it up

(waves) Author here! I wrote this article about some improvements to C++, but I also made a whole programming language [0] using a lot of these weird techniques. So not quite nobody!

Still, I can see why very few people do it. It's a massive undertaking. Even if one is fortunate enough to be able to spend the thousands of hours it takes to make a language, there's only a 0.0001% chance a particular language will even have a chance to make it into the mainstream. In other words, a glorious, glorious fool's errand.

One basically needs to be insane to embark on such an endeavor. But hey, turquoise bicycle shoe fins actualize radishes greenly!

[0] https://vale.dev/

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#140

Earlier quoted context omitted.

Not necessarily, although it's a bit complicated to understand in C++. Starting with C++17, there is a feature called guaranteed copy elision that works for many/most scenarios that you would want. You need to read through the following resources to understand it fully: https://en.cppreference.com/w/cpp/language/copy_elision https://en.cppreference.com/w/cpp/language/value_category

> Not necessarily, although it's a bit complicated to understand in C++. One could say this statement applies to most lines of C++ code. Lol

Indeed :)

Makes me appreciate the explicit copy() and ref semantics in Rust.

Although I bet in most cases such a method gets inlined so it doesn't matter.

Post reply on HN