For example, let's say you have a class that news up an object in its constructor. Presumably, the corresponding delete lives in the destructor. But if that constructor throws an exception, the destructor will never be called. If it throws the exception after the "new," then you have a memory leak.
So basically, you need a class whose only job is to manage a single pointer. You could certainly roll your own smart pointer, but it would have to work pretty much exactly like one of the ones in the standard library.
First, auto or scoped pointer gets you a stack discipline. If your objects weren't very big, you might as well create them on the stack itself. More efficient and suitable for multithreading. If the lifetime does not follow the lexical stack then you are out of luck with this style of RAII.
Next, reference counted pointers: Cycles are clearly a problem. The traditional mantra is: use weak pointers to break cycles, but then you are back to error prone manual management (of cycles). If I am to be trusted with using weak pointers correctly, I wouldn't be too bad with manual memory management either. Pure functional languages with eager semantics do not allow cycles, so are ref counts a good idea in such cases ?
Although, many do not think about it that way, reference counted system is a _garbage_collector_. Just a particularly simple one. Just because one understands how it works, does not disqualify it from being a garbage collection system. It is just not a very efficient garbage collector for cases where running time is sensitive to cache locality, which is true fairly often.
I am quite excited about Rust's regions. I dont think programming with regions is easy and you would want to have a garbage collector anyway. I hope this style of programming sees more mainstream visibility. Something should come out of all the MLton work on this. EDIT Indeed meant MLKit but it draws enough from MLton that I thought HNers would be able to relate.
There is also D's scoped guards, but I am not yet familiar with what they do.
I agree and I've been wanting to emphasize this too for a while.
I think it would help Rusts adoption a lot if it would get a good garbage collector while it's still young. Otherwise I think a lot of programmers will give up after stumbling around with borrow-checker errors when doing some things that are pretty trivial in other languages.
If we had a garbage collector, we could only worry about regions in the hot spots, that would ease many people's way into the language a lot, IMO.
But some types of algorithms which are allocation heavy (with dynamically sized parts) will just run much faster with a good collector, and are easier to write that way.
Also I've found that with regions, things get really complicated when structures contain borrows (and not every structure will logically own what it references), and then the structures become parameterized over the lifetimes, which multiply quickly.
For cases like that I'd rather start with a GC reference, and gradually remove GC references where time permits and profiling dictates.
I think Rust can really have the best of both worlds here.
BTW: I did manage to finish a fairly involved analysis program in Rust without using any GC, and I'm really very happy with the results. Being able to GC a few references would have made things easier though.
> For cases like that I'd rather start with a GC
> reference, and gradually remove GC references where time
> permits and profiling dictates.
Long ago this was one of the original theses of Rust, that you'd favor GC'd objects at first and then we'd provide easy ways to transition your code to use linear lifetimes where profiling deemed necessary.It took us a long time and boatloads of iteration to finally realize that this just wasn't feasible. The semantics of GC'd pointers and linear types are just too drastically different, especially with how they interact with mutability and inter-task communicability. We "officially" abandoned GC in the language itself last October, and we're still trying to wrest the last vestiges of its use out of the compiler itself. Ask pcwalton how much of his time has been spent doing exactly that in the past four months. Just yesterday one of our most tenacious community contributors proudly announced that an all-nighter had resulted in removing two more instances of GC'd pointers from the compiler, which required touching "only" a few thousand lines. It's a bad scene.
So yes, while Rust will still offer GC'd pointers in the standard library, we've long since learned not to encourage users to reach for them without great justification. You must understand that once you introduce garbage collection into your code, it will be very difficult to return from that path.
We've also, in practice, tended to point people toward RC'd pointers (with weak pointers to handle cycles) whenever anyone needs multiple references to the same piece of memory. Thanks to linear types, reference counting in Rust is actually surprisingly cheap (many instances where other languages would need to bump the refcount are simply achieved by moving the pointer in Rust, since we can statically guarantee that that's safe).
Good to hear about RC efficiencies, I assumed they were basically shared_pointer, I haven't tried using them and will give them a look.
EDIT: @detrino Ah! our typical sets are different then. How about "destructor dependent cleanup", call what we may, thats the underlying mechanism, but its too limiting.
Exactly. An awful one.
Also, ARC is not a refcounting GC, the ARC calls are injected statically in the binary as if they'd been inserted manually in the source.
A programming language is supposed to be small so that programmers can share code. The rest can be handled with libraries. That's why C is better for huge projects. Linus agrees with me :)
In addition, why encourage reference counting pointers? If you really want to manage objects on the heap, have a garbage collected reference counted pointer.
> transfer ownership
Then transfer ownership. The object that owns them encapsulates a container. When it is destroyed, it iterates the container and destroys the objects that it owns. So to transfer, take it out of that container and pass it to the new owner. It isn't rocket science.
>unknown lifetimes
Then you have a lazy, poor design. And that's the point really, managing your memory properly is an intellectual rigour - it forces you to improve your design, to be acutely aware of these issues. Not just spawn shared_ptrs and hope for the best. We have nice forgiving languages where you can be lazy. Coding in C# is like taking a holiday. C++ is meant to be hard.
I thought C++ was meant to be a lot of things but I don't think it being "hard" was one of the key points of its inception.
Just because you're using a "hard" language, it doesn't make you a better programmer. If the language can be made easier, more productive to write, with less problems and bugs due to memory leaks and inaccuracies, while not impacting performance, why wouldn't you want to do that?
I use many languages. I use C# to knock out a GUI. VBA for a bit of spreadsheet work. Q/KDB+ for database/functional work. R for stats. All of these are far more productive than C++. If you want fancy abstractions, other languages have them by the bucketload.
I don't use C++ to be productive, and I don't expect to be. I use it to get close to the metal and have full control. Everything you code will take 4-5 times longer to write. And longer to compile. You will be careful because you have to be careful, and that's a good thing because with that care comes quality.
Most C++ coders I've talked to (including all of the ones who get paid for doing it) are using C++ to get useful stuff built, not for the sake of 1337ness.
If you are making your programming harder to zero run-time benefit, you are a poor coder whatever skills you may have.