Memory models

You have indeed several memory models around:

  • Manual memory handling
    C and C++ provide very fine-grained control over memory allocation; heap memory can be explicitly allocated and de-allocated.
  • Full garbage collection
    The majority of modern languages expose a memory model that uses the heap for everything, ranging from Java to Go to JavaScript to Python to Ruby to Haskell.
  • Garbage collection with value types and references
    C# for instance is essentially garbage-collected, but features value types which are guaranteed to be stack-allocated if in local variables.
  • Manual memory handling with reference-counted types
    This is where Delphi shines with its Copy-On-Write (COW) types and reference-counted interface, and also Objective C with its ARC model (which is an more sophisticated approach to reference counting).
  • Safe manual memory management
    Rust falls into this category: you can choose where your object will be allocated (heap or stack), and how tasks work with it (it minimizes sharing by default, in contrast to other models).
As you can see, Rust is pretty unique in this panel.
But we will see that Delphi is not far away from it.

Rust memory model

Rust allocates objects in a task-oriented manner (extracted from their language definition):

Rust has a memory model centered around concurrently-executing tasks.
Thus its memory model and its concurrency model are best discussed simultaneously, as parts of each only make sense when considered from the perspective of the other.

When reading about the memory model, keep in mind that it is partitioned in order to support tasks; and when reading about tasks, keep in mind that their isolation and communication mechanisms are only possible due to the ownership and lifetime semantics of the memory model.

So the memory model is the following:

A Rust program's memory consists of a static set of items, a set of tasks each with its own stack, and a heap.
Immutable portions of the heap may be shared between tasks, mutable portions may not.

The immutability of memory portions does make sense, but I tend to like the Delphi COW approach very much.
For instance, local variables in Rust are immutable unless declared with let mut

Rust features a task-oriented memory model, which purpose is to avoid full Garbage-Collection memory model.
The basic idea is to remove garbage collection from the core language and relegate it to the standard library, with a minimal set of language hooks in place to allow for flexible, pluggable automatic memory management.

As a consequence, you can allocate your objects from the stack or the heap - this is a difficult concept to grasp for many programmers used to languages like Java or C# that don’t make such a distinction.

In Rust, a box is a reference to a heap allocation holding another value.

There are two kinds of boxes: managed boxes and owned boxes.

  • A managed box type or value is constructed by the prefix @
  • An owned box type or value is constructed by the prefix ~.

In short:

  • ~ is just the Rust equivalent of the C malloc and free functions;
  • @ is a replacement for manual reference counting in C programs (i.e. when your value need to be managed outside the immediate execution scope);
  • & will define a "borrowed pointer", i.e. a reference to the object.
As a consequence, Rust is able to use the stack or a task-specific heap to handle owned box values.
It results in a much better performance scalability, when used in concurrent mode.
For a more complete introduction to Rust's memory model, see this great article.

On Delphi side

Not object instead of class - but object in conjunction with class/interface for most high-level objects.

There are a lot of opportunities when it does make sense to have: 

  • Individual objects allocated locally on stack within a method/function scope;
  • Set of objects allocated at once on a [dynamic] array (local or shared);
  • Map some binary content with strongly-typed pointers and object methods.
You can do this with plain record, but object allow inheritance, which is IMHO mandatory to follow proper OOP design - the Single Responsibility principle, for instance.
In Domain-Driven Design, objects are great to define value objects.
With strong-typed pointers, your code can still be safe to work with (it won't suffer from C weakness of pointer typing).

In my opinion, the Delphi language, in its current state, features a large panel of 
Reducing the Delphi language to just one string type or just one memory model (like with the Delphi NextGen compiler) is not a good idea.
It sounds like a lack of vision to me. 

Some years ago, we were told by EMB that Garbage Collection was the future.
Now - thanks to the Apple hipe - the ARC model is the new model to follow.
When you take a wide look, e.g. when you look at Rust, you can see that a less monolithic approach (like the one existing on OldGen Delphi) could make sense!

From my understanding, the object pascal memory model (with dedicated class and object types) may be more easy to work with than the Rust @ ~ obfuscated syntax.
And you are not stuck by hard coded principles like default immutability of tasks: in Delphi, you can still use a global heap, and rely on dedicated structures when you want better performance.
This is what we tried to do with mORMot: high-level methods are easy to work with, but its internal core uses low-level tricks (like pointer arithmetic or stack-allocated structures) to ensure the best possible scalability.

In a practical point of view, Object Pascal's Manual memory handling with reference-counted types does still make sense.
In addition to the initial COW paradigm, the RCU (Read-Copy-Update) pattern could make sense: when used in multi-thread context, it allows lock-free shared access to resources (which is not allowed by Rust).

The proposal of a threadlocalvar compiler enhancement - in this very same blog, back in 2010 - still does make sense, and perfectly fit with the memory model proposed by Rust.
A custom mode of class allocation could make sense also, by overriding the low-level allocation/release model of TObject to allocate on the heap by default, but on the stack (with auto-release at the end of scope) with an optional pattern.

It is a pity that the object type is just broken since Delphi 2010.
Feel free to give your feedback on our forum, as usual.