Memory safe in D

Steven Schveighoffer schveiguy at gmail.com
Wed Mar 13 19:58:24 UTC 2024


On Wednesday, 13 March 2024 at 19:36:01 UTC, Alex wrote:
> On Wednesday, 13 March 2024 at 18:20:12 UTC, Steven 
> Schveighoffer wrote:
>> - The path D takes is, let the hardware solve it.
>> - The path Java takes is to instrument dereferences on 
>> possibly-null variables and throw an exception if it's null.
>> - The path many other languages take is to force you to 
>> validate it's not null before using it.
>
> Rust doesn't allow null references at all (exclude unsafe 
> code). It is one more alternative path.

Then "optional" types, basically the same thing. Null is a memory 
safe "invalid thing".

The thing I'm getting at is -- if you have something that doesn't 
exist, but is supposed to exist, then you have to deal with it. 
How you deal with it is where the tradeoffs come in.

The basic building block that all memory-safe language tools are 
built on is -- you should not be able to use invalid memory. null 
pointers which halt the program are a flavor of that.

>> For the other languages, you are forced to validate something 
>> is null or not null. This has the advantage that certain 
>> classes of bugs can be avoided at compile time, and in many 
>> cases, the code can be clearer where null pointers might 
>> exist. But the cost is that you may be forced to validate 
>> something that is obvious to the developer (but not the 
>> compiler). It adds to the tediousness of the language.
>
> On the other side with this approach developer can choose 
> between nullable type and non-nullable. If he choose nullable 
> type he really have to do checks. But for non-nullable type he 
> can work completely safety without any checks. As I know Kotlin 
> encourages using non-nullable types wherever possible.

The checks have to come somewhere.

If you have a value that is of *unknown* validity, and you want 
to ensure it's valid, you need a check. We have different flavors 
of:

* automated checks
* how the checks are handled if failure occurs
* checks that you are forced to perform
* how long such checks are enforced by the compiler (e.g., flow 
analysis, building into the type the validity).

Building non-null into the type indeed means as long as you have 
that type, you don't have to check. But to get it into that type, 
if you started with a possibly-invalid value, *somewhere* you had 
to do a check.

Consider an array/vector of items in Rust. And an index. When you 
index that vector, the compiler has no idea what that index is. 
It must validate the index before dereferencing the element. This 
is a check, and the handling of it is defined by the language.

Having a possibly-null pointer is no different. D defines that in 
safe code, a pointer will be valid or null. The "check" occurs on 
use, and is performed by the hardware.

This is in *stark* contrast to having a possibly-invalid pointer 
to non-null memory (e.g. dangling or buffer overflow). Those 
should never occur.

-Steve


More information about the Digitalmars-d mailing list