Hacker News new | ask | show | jobs
by chamomeal 480 days ago
Is that possible? Could C add a borrow checker? Honest question, I have no idea how anything works
4 comments

C takes backwards compatibility quite seriously, so anything it adds has to be opt-in (not that it stops some people from trying to propose seriously breaking changes, but c'est la vie).

Something like a borrow checker can be added (and there are people on the C committee willing to see it). However, Rust's "shared xor mutable" rules are probably too strong for C, so you'd need to find some weaker model that solves a good deal of memory safety issues (maybe expressing pointer ownership and some form of invalidation is sufficient, but this is just some spitballing by me). The focus by the interested people right now is mostly around bounds-checking rather than a borrow checker.

In theory, they could. In practice, I would be shocked. As an example, Rust's memory safety rules are fundamentally built on top of generics, which C does not have. So in order to copy what Rust does, they'd need to do that first, and that's a massive change to the language.

C++ does have generics already (well templates, but you know) and so it'd be an easier lift there, but it's still a lot of work. https://safecpp.org/draft.html explains how this would be possible.

> Could C add a borrow checker

As I understand it, doing it while maintaining compatibility with old code is not possible. You'd have to add new required syntax for annotations and such.

Not really possible, I think. C is a language that's basically built on memory aliasing and pointer arithmetic: every variable is represented as a location in memory, but there is no guarantee that each memory location is only represented once (there can be many variables pointing to the same memory address). The Rust borrow checker needs pretty much the opposite guarantee: every declared variable has full control over its memory location, and if two pieces of code need to share memory there needs to be an explicit protocol for delegating access.

And it's not like pointers are a rare occurrence in C. This mechanism is used pretty much everywhere: accessing array values, parameter pass-by-reference, function output parameters, string manipulation. There's no concept of function purity either, so no way to guarantee in the function definition that a function cannot outstep its bounds. Sure, there are certain safe coding conventions and rules about what you can or cannot do in a C program, but fundamentally proving that a certain memory location is only accessed through a certain variable is only possible by just running the program exhaustively -- or by confining yourself to a subset of C.

But when you only allow a subset of C, it's no longer "C with a borrow checker", especially given the ubiquitous use of pointers for standard language features. It quickly becomes "we hobbled C because we need more guarantees". To take a quote from the D manual [0], to guarantee memory safety you need to disallow:

  - Casts that break the type system.
  - Modification of pointer values.
  - Taking the address of a local variable or function parameter.
[0] https://dlang.org/spec/memory-safe-d.html
> C is a language that's basically built on memory aliasing

Additionally, C does actually have aliasing rules, but many projects, including the kernel, turn them off. Linus in particular does not think they're worthwhile.

These rules are different than Rust's, Rust also rejected these particular rules.

> To take a quote from the D manual [0], to guarantee memory safety you need to disallow

Just to be clear, this is the list for D, not in general. Rust is fine with you taking the address of a local variable or function parameter.

> C does actually have aliasing rules

If you're referring to e.g. gcc's -f[no-]strict-aliasing option, then that's more about type compatibility than about limiting the scope of memory aliasing in general. If you mean something else, I'm interested to hear more.

> this is the list for D, not in general

Yes, I know. But it's the first authoritative source I could think of on memory safety in C-like languages. I don't think the list is wrong for C proper, just probably not exhaustive.

> Rust is fine with you taking the address of a local variable

Yes! But circling back to the earlier point: in Rust you can do this specifically because the language has well-defined lifetime semantics on variables and ownership. And as such, Rust can guarantee that a) a pointer to a memory location does not outlive the scope of the memory allocation itself, and b) when two variables/pointers refer to the same memory location, there is a compiler-enforced protocol for accessing and mutating that memory.

> then that's more about type compatilibity than about limiting the scope of memory aliasing in general.

It's about limiting the ability of pointers to alias. The C standard even has this parenthetical:

> The intent of this list is to specify those circumstances in which an object can or cannot be aliased.

You are correct that it's a pretty narrow set of rules. There are only six items on that list. But it's still an aliasing rule.