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