Borrow Checking and Ownership Transfer
Richard (Rikki) Andrew Cattermole
richard at cattermole.co.nz
Fri May 9 18:28:11 UTC 2025
On 10/05/2025 5:17 AM, jmh530 wrote:
> To your point about being a property of a pointer, the alternative
> approach is to introduce a reference that is borrow checked. Yes, it
> results in additional pointer types, but if that's the approach that
> Rust ultimately relies on and it's the only way to achieve what we want,
> then it's what should be done.
I want to touch upon this because its misunderstood quite widely
regarding the difference between Rust's borrow checker and ownership
transfer system.
The borrow checker loosens restrictions placed upon you by the ownership
transfer system to give strong guarantees of aliasing and lifetimes. If
you did not have the borrow checker, the ownership transfer systems
restrictions would make things absolutely impossible to work with.
I'll translate this into D-speak and I'm sure I haven't got it 100%.
For the purposes of this discussion I'll use a non-transitive type
qualifier called ``unique`` to indicate ownership transfer, but it can
be done other ways like reference counting or isolated from Midori.
When ownership transfer has a variable copied into another, the old
variable resets (back to null). You can only have a modelable number of
instances of this pointer within a function (could be just one).
By default, any variable (that is a pointer) that is unattributed is
equivalent to writing ``unique(int*)``, and ``unique(unique(int*)*)``.
If you add escape analysis annotations it becomes ``int*`` or
``unique(int*)*``.
The former can be passed to the latter implicitly.
```d
void goingDown(unique(unique(int*)*) input) {
goingUp(input);
}
void goingUp(scope unique(int*)* input) {
unique(int*) v = *input;
}
```
Now the escape analysis, first you have the going up the stack
guarantees, this can be seen in ``goingUp``.
It is where the compiler is establishing the relationships between the
variables as seeable by a function prototype.
This is what our DIP1000 attempts to do poorly.
The second kind is in ``goingDown``, and goes by the name borrow checker
or as I prefer it "owner escape analysis" and this is what "@live"
supposedly fills the gap of (but doesn't).
It adds some extra logic to escape analysis that ``goingUp`` uses,
specifically relating to the number of mutable copies you have of it,
and requires that object will outlive its borrow.
```d
unique(int*) owner;
{
int* borrow1 = owner; // ok
int* borrow2 = owner; // Error
const(int)* borrow3 = owner; // ok
owner= null; // Error
}
```
Its important to note here that something has to trigger owner escape
analysis. Either the type qualifier or the rest of the escape analysis
(via say a function call).
More information about the Digitalmars-d
mailing list