Live data from Hacker News

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

verdagon.dev

171–180 of 226 posts

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

#171

Earlier quoted context omitted.

Right but it’s that 5% the origin comment is talking about. The times when rust has to use unsafe for the type of program.

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

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

#172
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…

Often missed here is that the Rust library author is strongly protected from faulty code written by Users. The C/C++ library author is not. The most obvious examples of this are memory allocation. The C/C++ user claimed the buffer was large enough to contain the result. The Rust user received back an object that protected the memory and returned it at the right time. But it could also be a file handle or mutex that u…

That may be true but it doesn't help downstream users know what they're opting into. Knowing that a rust library can use no unsafe code is not the same as knowing that that library did use no unsafe code, in a similar way to knowing that a C library can be coded with the help of various tools to provide additional safery (in some cases beyond what rust provides natively) is not the same as knowing they did.

In other words, that something was written in a specific language is far too coarse and blunt an assessment to really know what you're getting into.

Rust libraries may be, in aggregate, far more likely to have additional safety than C libraries because of features rust provides that are generally used, but you can know nothing about the specific relative safety of a single rust library and C library without looking closer, at the code level. Having indicators of exactly that info surfaced in descriptions about libraries would be a major step forward IMO. I think it would also probably immediately benefit rust if it were to happen, as the info would cast many rust libraries in a beneficial light in comparison to others, but to me that's far less important than promoting better practices overall regardless of language by allowing users to better choose between them, regardless of language.

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

#173

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…

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.

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

#174
post #62

Earlier quoted context omitted.

Yes it’s really undefined. There is a distinction from “implementation defined behavior” which you seem to be confusing it with. You are practically wrong in your assumptions. Since undefined behavior is undefined the compiler is free to do anything with compilation, it may compile to something but you have no guarantee what that something is. And in real life this often actually bites you when the optimizer comes in…

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 "format your hard drive and blow up your monitor," someone would have tripped over it testing the compiler and changed the compiler.

This is incorrect. In one very popular web server, some behavior depends on the values set for some response headers, and the value is checked in part by calling a function like strstr which takes two pointers and two lengths and searches one string for the other string. If you pass {null, 0} as the haystack and the implementation of the strstr-like function starts by computing the upper bound of the haystack (null + 0) then the compiler can legally produce *any value* for that expression. Then your program will quite predictably segfault. When it does your recourse is to fix your program, not to fix the compiler, because the compiler is working correctly.

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

#175
Well, what's new here?

Generational References, random or sequential

That's an old idea. Goes back to at least the 1980s. It's a useful way to detect use-after-free at run time, but doesn't prevent it. "To get access to an object, we first check ("generation check") that the current generation number matches the remembered generation number. If not, we safely signal a segmentation fault." Um.

Rule 5: We can only read a field by taking ownership of it, by either swapping something into its place or destroying the containing struct.

Hm. This is single ownership with move semantics on steroids. There was once some enthusiasm for languages where you could only read a variable once. But that didn't go very far. On the other hand, single assignment, where you can only write once, on the other hand, is now widely accepted and useful.

I doubt that any collection of hacks on top of C++ will make it safe. Too many people, including me, have tried. To increase safety, you have to take things out. This is unpopular and breaks backwards compatibility.

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

#176

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

> Borrow checking is incompatible with some useful patterns.

The main problem is back references, as in doubly linked lists. In Rust, you can do that sort of thing using Rc and the weak/strong reference mechanism. Forward references own, and are strong. Back references are weak.

I've been toying with the idea of some generic types which allow strong forward references which you can't copy or clone, and weak back references which you can't make strong outside a contained scope. This can be implemented with the existing Rc system, and potentially could be proven, with a static analyzer, to not require the reference counts. It's worth a try to see if one can effectively program under those restrictions. If it's not too much of a pain to use, this might be an effective way out of Rust's back-reference problem.

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

#177

Earlier quoted context omitted.

Exactly. Presumably there are some opt-in tools that are very strict about the inline assembly uses you have in C. I don't have any experience with them, but I'm sure they exist. That's the whole point, it's less about the language, and more about the specifics of the code itself in a testable way. Saying something is written in C without accounting for a bunch of inline assembly is analogous to saying something is w…

There are no Rust crates with 95% unsafe code.

Crates that only make FFI calls exist. I have written some. :)

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

#178
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…

> If the choice is a Rust library with 95% of the code in unsafe blocks, ... Rust is not providing any real safety benefits in that situation, is it? If the choice is a C library with 95% of the code in inline assembly, ... C is not providing any benefits in that situation, is it?

People have this misconception that unsafe Rust is some other language. It's not. All it does is allow the use of raw pointers. So, all other Rust feature (ie type system) still work as they always did. So, you can really minimia the surface are of unsafe code. Not to mention seeing unsafe just means that something really low level is happening, and signals caution to the reader (or anyone editing the code), as opose to C++ where it's easy to forget you need to be alert, since you should be alert all the time really.

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

#179

Earlier quoted context omitted.

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

The core value proposition of rust is that it’s memory safe by default , and it’s possible to limit the set of code that needs to be manually checked for UB. This isn’t the case for C++, as any code anywhere can invoke undefined behavior.

True, as long static analysers aren't part of the build, at which time specific constructs can be made to break the CI/CD build, forcing everyone to play by the rules if they want the PR to go through.

It isn't perfect, but does improve a lot the security baseline.

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

#180
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…

> "unsafe" just means "safe but the compiler cannot verify it".

"unsafe" means "safe"?

I would say "unsafe" means "only safe if used in a manner that cannot be checked by the compiler".

Post reply on HN