3 ms·
> Lifetimes aren't a worse version of GC, just like static typing isn't a worse version of dynamic typing. They're different trade-offs. This is a completely b
by h91wka 7y ago
> Lifetimes aren't a worse version of GC, just like static typing isn't a worse version of dynamic typing. They're different trade-offs.
This is a completely bogus comparison.
First, static typing is mostly about finding bugs. GC-induced overhead is not a _bug_.
Second, it is theoretically impossible to create an algorithm that checks almost any interesting property of a program in a Turing-complete language in finite time, that doesn't reject valid programs. In most (non-academic) programming languages with static typing compiler checks assumptions about the _data_, and doesn't wade in control flow at all. Data is more or less "static" by itself (types of variables remain the same in most algorithms, with exceptions covered by ADTs), that's why static typing is practically useful.
Lifetime control is a problem on a whole different level. Roughly speaking, lifetime of an object _easily_ depends on whether or not some part of the program terminates, so, from the get-go, your static checker has to solve halting problem, which it can't.
I went through several Rust projects to see how they deal with this. And there are two solutions: 1) sprinkling code with unsafe's 2) resorting to reference counting. GC is safer than both alternatives, and its amortized cost may be less than same for ARC.
Absolute majority of the code written doesn't have hard real-time requirements, and should just rely GC as a safer and more convenient option.
- thelazydogsback 7y ago> Absolute majority of the code written doesn't have hard real-time requirements, and should just rely GC as a safer and more convenient option Yes. Most Rust code deals with "trivial" lifetime issues -- not trivial in the the sense of how it gets used (usage can still be in a "serious" context, such as systems programming) but how the lifetime is controlled. Only certain models lend themselves to be expressed as "borrowing from an owner", and ref-counting will break with general object graphs. Most garbage is gen-0, which gets recycled with little overhead, and when scavenging is needed its fairly quick and tunable these days and can optionally take advantage of multiple cores. Another "trick" I see in Rust code is to use a symbol-table of sorts to represent object graphs using names and lookups, but you're just inventing your own, slower version of object references, and then there's the issue of who owns the graph symbol table, so you're only passing the buck in the general case.
- pornel 7y agoLifetimes and ownership are about finding use-after-free and double-free bugs. The fact that they enable automatic memory management without a tracing GC overhead is just a cherry on top. For a long time languages were stuck with the false dichotomy of either trying to be 100% safe (and pay the cost of a VM/sandbox/GC) or said "halting problem" and gave up. Rust solved this dilemma by allowing safe abstractions around unsafe blocks. It works like a charm — I get speed I got from C, but don't have to use gdb. There's option 3) use program architecture that has clearer ownership and less shared mutable state. Borrow checker doesn't like doubly linked lists or mutual parent-child relationships. Instead you use different containers and DAG structures. And when you still have to have shared ownership without a clear scope, then you use refcounting. The nice thing in Rust is that (unlike C++/ObjC/Swift) it doesn't always have to be atomic in multi-threaded programs, and objects can be borrowed and used without touching the refcount.
- pjmlp 7y agoWhat false dichotomy? Modula-3, Mesa/Cedar, Component Pascal, System C#, ... You get the productivity of automatic memory management, RAII, stack and global memory allocation and unsafe blocks just like Rust, with C's speed as well.