Live data from Hacker News

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

verdagon.dev

51–60 of 226 posts

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

#51

The reason why safety in C++ is difficult to achieve is due to the memory model used by C and C++. The memory model is a flat space provided by the OS that can be addressed by pointers. In this sense, C++ is similar to assembly code. A language like Java, on the other hand, assumes a different model where you can only access objects with well defined behavior. To change this, one needs to disallow the use of native p…

It depends what you mean exactly. The C and C++ official memory model is very much not a flat space, but exactly what you describe for Java - you can only (validly) access objects. For example, the operation x int x = 0; int y = 0; if(&x Now of course the implementation of C and C++ actually assumes without checking that you only access objects and not raw memory, and thus will happily read raw memory directly.

I really feel like it's a hell of a definitions dodge to say "This is what the model is" when no compiler implements constraints to require the user to treat the model like that (i.e. I can always just increment the pointer, or typecast it to numeric type, do math on it, and typecast back to a pointer, without having to pull any big red levers like using "unsafe" methods).

If it's undefined but it compiles to something, is it really undefined, or is the definition merely not standardized?

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

#52

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.

this is because programming languages have network effects, and are costly to move and test in real world case, you can use pony, but luck searching sdk, databases, performant compilers, and maintained libraries, the community aspect of programming languages ecosystems makes this, no matters how great it is if inst popular you will have hard time being a developer in it. that why most languages that works start in niche great scripting, good for data analysis, great for concurrent programming scala, and some of then like python then scale and other like scala or julia don't.

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

#54
post #8
post #7

Earlier quoted context omitted.

Ideally we would have -fsafe and [[unsafe]], but it will take years for something like that.

Presuming syntax for “unsafe” that gracefully degrades in non-aware compilers, why couldn’t a particular compiler start doing it right now, starting with a very trivial safety checker than can be iteratively improved upon once the framework is in place?

It is easy to say add unsafe. However the details are very complex. I've read a few of the papers proposing something like this, and they spend a lot of time discussing some nasty details that are important to get right.

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

#55
post #27

The reason why safety in C++ is difficult to achieve is due to the memory model used by C and C++. The memory model is a flat space provided by the OS that can be addressed by pointers. In this sense, C++ is similar to assembly code. A language like Java, on the other hand, assumes a different model where you can only access objects with well defined behavior. To change this, one needs to disallow the use of native p…

> The memory model is a flat space provided by the OS that can be addressed by pointers From what I understand this is not true. Pointers cease to be valid the moment you try to leave a single allocation. You get to play around within a single continuous allocation and one past the end, everything further out is playing with fire. Even comparing the "addresses" of two separate allocations is undefined if done with "…

> everything further out is playing with fire.

That's the point. C and C++ don't prevent you from playing with that for. Memory-safe language do.

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

#56
post #49

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.

Because implementing a new language and getting it to wide adoption is an enormously challenging task, with a much lower success rate than e.g. SV startups. Languages that try to implement one new bright idea don't go anywhere, because that's not enough to cause people to switch. At best they serve as examples for feature adoption in other languages. Look at Rust for example: it seems to be succeeding and gaining ado…

> 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 still increasing, at least in specific areas, and has not leveled off or tapered. It doesn't seem to be exploding into lots of teams and places, but it does seem to be getting footholds still, like at Azure.

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

#57
> 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 pattern in Rust and I make use of it all the time; the library for making use of it is `slotmap`.

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

#58

> Tracing GC is the simplest model for the user, and helps with time management and development velocity, two very important aspects of software engineering. > Borrow checking is very fast, and helps avoid data races. One thing many people seem to assume is that not having to care about memory means you can program faster and get to your goal faster. As the author here seems to do. However as it turns out, if your pr…

Two things, full time Rust dev here:

a) Rust's borrow checker is good and its type system good, but IMHO it's not really doing what you say it is as well as you're implying: "explaining in an explicit way who owns what"; While ownership is explicit and static (apart from RefCell and friends), description of that ownership is scattered all over, program state flows are not modelled in the type system at all, and on the whole Rust is far from having being a kind of explicit "I can reason about the whole program" declaritive system with the kind of clarity you're implying. Or maybe I'm taking your claims too strongly.

b) Rust's borrow checker is good. But it's not perfect and fails to pass things that in fact should be legal borrows. In particular there's edge cases around where things are grabbed in if/let/else or matches, like this fail (from my own code):

        {
            let local_version = self.seek_local(tx);
            if local_version.is_some() {
                return match &local_version.unwrap().value {
                    Entry::Value(v) => Some(v),  // reference to value
                    Entry::Tombstone => None,
                };
            }         
        }
        // note that 'local' has gone out of scope here and so self should not be borrowed 
... code later in func complains 'self' is still borrowed,

but the same thing done this way (but less efficiently) passes:

        if self.seek_local(tx).is_some() {
            let local_version = self.seek_local(tx).unwrap();
            return match &local_version.value {
                Entry::Value(v) => Some(v),
                Entry::Tombstone => None,
            };
        }
... same other code that uses 'self' compiles fine

In neither case is the 'local_version' being used outside of the lexical scope, and 'self' cannot be borrowed in either case, but the borrow checker is convinced in version #1 that they are and that code below that lexical scope cannot proceed because 'self' is borrowed. They're logically basically equivalent from a program flow and state mgmt, but the second passes while the first fails. Rust 1.7.0 stable.

(Before you ask, I did have if/let to take apart local_version instead of using unwrap, and the compiler griped about that even more)

Having the burden of how to fix that fall on the programmer sucks. This is all a step in the right direction, but I run into this kind of thing here and there and I shouldn't have to.

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

#59

The reason why safety in C++ is difficult to achieve is due to the memory model used by C and C++. The memory model is a flat space provided by the OS that can be addressed by pointers. In this sense, C++ is similar to assembly code. A language like Java, on the other hand, assumes a different model where you can only access objects with well defined behavior. To change this, one needs to disallow the use of native p…

It depends what you mean exactly. The C and C++ official memory model is very much not a flat space, but exactly what you describe for Java - you can only (validly) access objects. For example, the operation x int x = 0; int y = 0; if(&x Now of course the implementation of C and C++ actually assumes without checking that you only access objects and not raw memory, and thus will happily read raw memory directly.

The result of the pointer comparison is unspecified, this is not undefined behavior in C++.

I don't know about C.

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

#60
post #14

Earlier quoted context omitted.

I don't write Rust. But here is what you said and what the author said don't conflict with each other, and it has been on my mind for a while. People who write similar code, or work on things for decades usually don't really think through what "sketch out some code" looks like. They spend most of their time on refactoring things that has clear use-cases, but not well-defined API boundaries within the component, or be…

In my experience even in those "sketching" areas static types and strict checking is the better trade-off. I think the real criteria for "will static types and stricter checks help?" is "how long will this thing last for?". E.g. for a shell REPL you definitely don't want to have to write our types, but for a shell script you definitely do. Something like using MATLAB for exploratory research is probably another decen…

In your framing there's a sort of implicit downplaying of the frequency of exploratory work and an implicit promotion of stricter work.

> Something like using MATLAB for exploratory research is probably another decent example. Or maybe hackathon games. But for most games, data analysis, machine learning etc. then being stricter pays for itself almost immediately.

(Emphasis mine)

This is where the viewpoints differ. Some people spend a lot more time on the exploratory aspect of coding. Others prefer seeing a program or a system to completion. It largely depends on what you work on and where your preferences lie.

Years ago I wrote a script that grabs a bunch of stuff from the HN API, does some aggregation and processing, and makes a visualization out of them. I wrote it because the idea hit me on a whim while intoxicated, and I wrote the whole thing while intoxicated. The script works and I still use it frequently. I haven't made any changes to it because it just does what it needs to. It has no types. It's written decently because I've been coding for a long time but I was intoxicated when I wrote it. The important thing is it's still providing value.

There's a surprising amount of automation and glue code that doesn't need the correctness of a type system. I've written lots of stuff like this over the years that I use weekly, sometimes daily, that I've never had to revisit because they just work. I suspect it's a matter of personal preference how much time a person spends on that kind of work vs building out large, correct systems. I suspect there's a long tail of quality-of-life tooling that is simple and exploratory in nature much like large, strict systems are much bigger than most people expect at first blush because of how many cases they handle.

I think trying to say that one is more common than the other without anything approaching the rigor of at least a computing survey is really just to use your gut to make generalizations. Which is what the strict vs loose typing online debates really are. A popularity contest of what kind of software people like to write given the forum the question is being discussed on.

Post reply on HN