Live data from Hacker News

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

verdagon.dev

201–210 of 226 posts

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

#201

Earlier quoted context omitted.

Unsafe Rust is safer than C++, and even if it wasn't, 5% unsafe in Rust programs (in well-marked locations) is vastly superior to 100% unsafe in C++ programs. Any analogy that equates the two is silly.

unsafe rust is less safe than C++ because of the provenance and aliasing semantics that unsafe rust must adhere to to avoid UB, which are generally tricker than those of C++

The provenance rules in the C++ standard are basically just a shrug emoji†, so it's unclear whether those are worse, I can see an argument for the idea that obeying Aria's strict provenance experiment rules in Rust is easier - not because it's easy (although for many common cases it is) but because at least these are coherent rules.

† U+1F937 person shrugging

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

#202

Earlier quoted context omitted.

Did you get a look at https://github.com/rust-lang/rfcs/pull/3446 at all?

I'm not in the habit of regularly following new Rust RFCs, so I'd have no way of knowing about something made just last week. :-) But I'm taking a look now.

I don't tend to follow them either, but I've been frustrated by the lack of progress on allocator_api, and I came across this yesterday after looking into that. I only mention it because the Handle stuff in there looked tangentially related, though it's talking about something quite a bit different than you were.

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

#203

Earlier quoted context omitted.

No; this is a common misconception I see from people who swallowed the "it's allowed to format your hard drive and blow up your monitor" dodge vs. the electrical engineers who know where terminology like 'undefined behavior' originated in engineering. In practice, it tends to do something subtle and usually right but probably wrong for the simple, practical reason that if it did anything as obviously wrong as "format…

> No; this is a common misconception I see from people who swallowed the "it's allowed to format your hard drive and blow up your monitor" dodge vs. the electrical engineers who know where terminology like 'undefined behavior' originated in engineering. In practice, it tends to do something subtle and usually right but probably wrong for the simple, practical reason that if it did anything as obviously wrong as "form…

Oh, no argument here. All I'm saying is that the behavior resulting is determined by the current state of the machine and the compiler used.

Why does the distinction matter? Because I don't believe any program beyond minimal complexity written in C++ is actually free of undefined behavior. Therefore, being able to ask questions like " What configuration of compiler built this?" is meaningful for debugging code.

Because undefined behavior is so easily reached in the language's specification, the abstraction is broken from the start and one must know implementation details of the compilers used to understand how the code behaves.

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

#204

Earlier quoted context omitted.

Sure, but I think the point here is the degree. Presumably if it takes a lot of unsafe rust lines to build something, it won’t matter if it’s 30% safe or whatever. I just see the point of “unsafe is fine” a lot when the whole point of rust is that memory safety issues are never worth the cost.

Looking at a couple of programs I work on: 9,500 lines of code, 8 are unsafe. 7,000 lines of code, 22 are unsafe. 14,000 lines of code, 140 are unsafe. As we follow the standard rust rule that "safe code should not be able to use unsafe code to do unsafe things", those unsafe bits of code have been very carefully checked, to the best of our abilities, to ensure they don't create memory safety issues. It is a lot easi…

A counterpoint that makes this argument a bit weaker: Rust’s “unsafe” marker doesn’t pollute only its scope and actually pollutes the whole module; You need to make sure that the invariants in unsafe code are met even in safe code. (An explanation of this in the Rustonomicon: https://doc.rust-lang.org/nomicon/working-with-unsafe.html)

So there’s quite a lot more code to actually check then what some of the Rust proponents are saying. One can say that C++ is still worse in this regard (theoretically you need to check 100% of your code to be safe in C++). But for some minority of developers who frequently needs to delve into unsafe code, the advantages of Rust might seem a bit more disappointing (“the compiler doesn’t really do that much for the more important stuff…”)

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

#205

Earlier quoted context omitted.

Looking at a couple of programs I work on: 9,500 lines of code, 8 are unsafe. 7,000 lines of code, 22 are unsafe. 14,000 lines of code, 140 are unsafe. As we follow the standard rust rule that "safe code should not be able to use unsafe code to do unsafe things", those unsafe bits of code have been very carefully checked, to the best of our abilities, to ensure they don't create memory safety issues. It is a lot easi…

Sharpview, my metaverse viewer: 36,000 lines, 0 are unsafe. I use some published crates that have unsafe code, but my own programs start with #![forbid(unsafe_code)] This is 60FPS 3D high-detail graphics stuff, where performance matters.

Are you using wgpu for the rendering stuff? Heard that WebGPU had to sacrifice some performance in order to make the API safer for the web (like more bounds checking and sanity checks). These kinds of issues are actually plaguing projects like Tensorflow.js (for example see https://github.com/gpuweb/gpuweb/issues/1202).

Other libraries like Vulkan and DirectX 12 are fundamentally unsafe in the API level, so direct usage of it would lead to heaps of unsafe Rust code. Rust people have tried wrapping it in a safe way (like gfx-rs and vulkano) but nowadays most seem to have transitioned to wgpu (since WebGPU API is safe by design so it fits more for the Rust ecosystem).

Rust does sacrifice some performance in general in order to achieve its safety claims, but people are happy with it so far, since the majority of applications using Rust (like CLI apps and web servers) don't have to squeeze out performance that much (for webdev there are too many things that can cause performance issues other than not writing it in Rust). But for 3D graphics people can be more sensitive about these problems. Though maybe if you're not developing a triple-A game with the latest cutting-edge graphics (with new techniques like "hardware ray tracing" and "bindless descriptors", which are both impossible in wgpu), writing in Rust can be a good-enough tradeoff for your needs.

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

#206

Earlier quoted context omitted.

> You build safe abstractions around the ugly low-level pointer handling, you just don't have a keyword for a clear delineation. The main difference is they are not really safe. It is trivial to accidentally invoke UB with incorrect use of "safe" abstractions in C++ like built-in containers or smart pointers. Keep a reference to a vector element, add a new item to the vector and it will sometimes blow up ;)

I disagree that it is "trivial," at least in the example you stated. This take-reference-then-mutate is exactly the kind of usage that the borrow checker prevents. You have to avoid it systematically in both languages. The built-in containers are also not the best examples of "safe" abstractions. You can build safer abstractions, and you can employ safer usage patterns of built-in vectors, at non-zero but marginal co…

It’s subtle, but you don't avoid “take reference then mutate” in Rust, you are told exactly how to do it without aliasing the memory.

I’m not going to say Rust is perfect, that’s obviously not the case. But I really think your argument, like others are saying, underplays the actual value of Rust.

I’ve written entire projects in both C++ and Rust. I’ve never wasted days debugging memory corruption in Rust. Just sayin’.

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

#207
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?

I have implemented my own slotmap crate for a lisp interpreter that uses no unsafe code and provides exactly the same features as the "standard" slotmap crate. There is nothing inherent to the slotmap that requires unsafe code! It's only used for optimizations purposes. Mine works in a similar way to the "standard" slotmap. It's a vec of slots, slot is an enum that can be occupied or vacant, the occupied variant is a…

[deleted]

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

#208
post #136
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. 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.

> 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.

I think that might actually be worse though, performance aside. You're performing memory / object lifetime management but the Rust borrow checker still would have no idea what's going on because now you've tricked it by using indices or an opaque handle instead of references. The program may compile just fine but could have use-after-free bugs.

At least with unsafe there's an explicit acknowledgement that the borrow checker is turned off.

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

#209
post #63

Methods such as these for C and C++ are interesting, and needed, but only solve a part of the problem. As others have noted before, they do little good because they're opt-in. I think there's a bit of nuance to that which needs to be explored though, as I think it's less a problem that the extra checks are opt in, and more a problem of how we use and categorize libraries. As long as we encourage dynamic and static li…

I feel like a few languages are better than others in a related but not quite identical area: Languages like Java and Go, while they CAN escape to native libraries, have cultures that tend to avoid that kind of thing. At least, in my projects, I have quite an easy time using zero native dependencies with those languages (except for the underlying kernel of course), and so I feel like there is a much lower chance of e…

This is at least partially because there is a performance overhead to calling out to C from Java or Go so eventually the optimal implementation of something will be in the language itself. Rust's zero-cost calls to/from C is a positive in that it gives you a large ecosystem of existing code to use but a negative in that people are more likely to just try to build "safe" wrappers around those instead of writing something safe in Rust. This is somewhat countered by the "rewrite it in Rust" folks but you still get more C wrappers than Java or Go.

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

#210

Earlier quoted context omitted.

Sharpview, my metaverse viewer: 36,000 lines, 0 are unsafe. I use some published crates that have unsafe code, but my own programs start with #![forbid(unsafe_code)] This is 60FPS 3D high-detail graphics stuff, where performance matters.

Are you using wgpu for the rendering stuff? Heard that WebGPU had to sacrifice some performance in order to make the API safer for the web (like more bounds checking and sanity checks). These kinds of issues are actually plaguing projects like Tensorflow.js (for example see https://github.com/gpuweb/gpuweb/issues/1202 ). Other libraries like Vulkan and DirectX 12 are fundamentally unsafe in the API level, so direct u…

WGPU is just finishing up a major reorganization of locking and internal memory management, going from a global lock to fine-grained Arc reference counts.[1] Change log, just posted a few minutes ago: "Arcanization of wgpu core resources: Removed 'Token' and 'LifeTime' related management, removed 'RefCount' and 'MultiRefCount' in favour of using only 'Arc' internal reference count, removing mut from resources and added instead internal members locks on demand or atomics operations, resources now implement Drop and destroy stuff when last 'Arc' resources is released, resources hold an 'Arc' in order to be able to implement Drop, resources have an utility to retrieve the id of the resource itself, removed all guards and just retrive the 'Arc' needed on-demand to unlock registry of resources asap removing locking from hot paths."

From a performance standpoint, I'm much more concerned about being able to get all the CPUs working on the problem than slight improvements in per-CPU performance. My metaverse viewer has slow frames because loading content into the GPU from outside the rendering thread blocks the rendering thread. All that "ARCcanization" should fix that.

(I'm a user of WGPU, not one of its developers.)

[1] https://github.com/gfx-rs/wgpu/pull/3626/files/5b34df5a2e6f9...

Post reply on HN