Controlling how resources are allocated, used and deallocated is a tough job. There are of course schemes in place such as Garbage Collection, Reference counting and of course handling it all manually. Each of these incur their own costs. Some is small and often, others are rare but expensive. There is never a right answer to all use cases, but there is a set of good options for a particular one. From this I have come up with a design which I believe to take the best of both worlds.
The managed memory concept, has nothing particularly compelling or new about it, except how it is put together. The core things to understand in it are:
- The memory allocator
- Reference counting mechanism
- The memory manager
- How the value is scoped in its returning
- What transforms it
Lets start with the last one on the list, "What transforms it". There is a couple of things that should modify its type:
- Qualifiers add: const/immutable.
- Classes: T -> U where T is the more specific version of U.
- Arrays (basic types): T[] -> U[] where T and U are the same size.
- Arrays (classes): T[] -> U[] where T is the more specific version of U.
In the above list, no mention of structs or unions exist. That is because apart from special syntax like alias this (which should be banned here) there is nothing that can be done in those transformations to it. Further more these transformations take place using the cast syntax. Such as managed!int v2 = cast(managed!int)v;. If they cannot take place (for one requested), it should error out at compile time. Ensuring all valid conversions. This is why classes cannot be transformed from a more generic one to more specific. This gives you quite a bit of safety and security around casting that is guaranteed should it compile at all.
Next how the reference counting mechanism works can be entirely defined by the language but ultimately all that is required for this to operate is a simple call to increment + decrement functions that are defined. The memory allocator is again fairly straight forward, a class of some kind that allocates and deallocates at the request of a user. This could be a garbage collector or just malloc + free will do nicely.
Scope is another little language feature that is required for this to operate correctly. See, just because you have a type wrapped up, doesn't mean you can actually use it. This is where some support of alias this is very useful. Where by the managed memory type can behave as the specified type directly. Now if scope is applied something very brilliant can happen. The stored value can be accessed, modified but cannot escape from the control of the managed memory abstraction. But if a function won't escape it, it can take it implicitly. No conversion or being aware that it is under managed memory protection.
So far I have only managed to come up with two scenarios where the memory managers make sense. Basically what they can do is hook into the reference counter increment + decrement functions and allow you to do stuff like actually have a reference counter or deallocate a container not just the values. Which is useful if you want to seperate out a value in a container from all the values in said container.
When supporting arrays, accessing indevidual elements, they should return as managed types. Preventing escaping and other risky things. Other actions such as growth and reduction should be wrapped to force the specific allocator to handle it. But the desirability of support for arrays as anything more than read-only, may not be desirable in most situations.
During interactions of external reference counting schemes, it may be quite annoying to typing out the add reference + remove reference calls. So managed memory implementations should support these approprietely. If these are done, it almost always is desirable to not let it the managed memory type to deallocate. Instead have the object do it, itself. Which is a very nice convenience feature.
Over all this concept does provide quite a bit of functionality and security around memory usage. But it isn't fit for every purpose. Its best for dealing with external resources such as windowing or COM objects.