@trusted attribute should be replaced with @trusted blocks

Ogi ogion.art at gmail.com
Wed Jan 15 14:30:02 UTC 2020


There was a discussion a few years ago [1], but now it came up 
again in the light of @safe-by-default DIP. I’ve created a new 
thread for this subject because it’s off-topic to DIP 1028 but 
still deserves attention, especially now when there’s a shift 
towards memory safety.

This proposal is independent to DIP 1028. We can have @trusted 
blocks without @safe by default and vice versa. But they go along 
nicely.

The idea is to remove @trusted as a function attribute and 
instead introduce @trusted blocks:

@safe fun() {
     //safe code here
     @trusted {
         //those few lines that require manual checking
     }
     //we are safe again
}

Generally, only a few lines inside @trusted are actually unsafe. 
With @trusted blocks parts that require manual checking will 
stand out. Why should we leave the task of identifying 
problematic places to the reader?

Once the dangerous parts are enclosed inside @trusted blocks, 
memory safety of the rest of the function is guaranteed by a 
compiler. Why should we refuse from the additional checks?

Actually, there’s already a workaround for injecting unsafe 
section to @safe code: just put it inside @trusted lambda that’s 
called immediately. It’s featured in the “How to Write @trusted 
Code” in D blog. Even Walter admits that he’s using this trick in 
Phobos. So why should we resort to dirty hacks that could also 
harm performance and compilation time if we can make it part of a 
language?

Some would say that we need @trusted attribute to distinguish 
functions that are checked by compiler and functions that are 
checked manually. But is @safe actually safe? You can’t tell that 
a “safe” function doesn’t call @trusted functions somewhere along 
the road, or doesn’t use the aforementioned lambda hack. Why 
pretend that @safe guarantees safety if it’s nothing more than a 
pinky promise?

If you think of it, it makes no sense for @trusted to be a 
function attribute. It doesn’t describe its behavior but its 
implementation. If you call a function, it should not be your 
concern; @safe and @trusted are the same thing for you. But these 
two attributes result in two different signatures. If a function 
expects a @safe callback, you can’t pass a @trusted function to 
it without @safe wrapping. If some library makes a function that 
used to be @safe @trusted, that’s a breaking change. Etc, etc. 
Why should we introduce complexity out of nowhere?

Without @trusted the safety design will be much simpler to grasp. 
Currently the are three vague keywords, complex rules on which 
functions can call which and best practices on which attribute 
you should choose. Without @trusted, it’s simple as potato: there 
are @safe and @system functions, @safe require putting unsafe 
parts inside @trusted blocks and can only call other @safe 
functions. We should not disregard the learning experience, 
because with every overcomplicated aspect of the language we lose 
some potential users.

With @safe as default upon us, it’s especially important to get 
safety right.

There is a risk that users would slap @trusted on everything just 
to make it work. While the new syntax wouldn’t stop them all, 
putting a whole function (or an entire module) inside of a block 
would scream code smell.

We can’t expect all users to be world-class programmers. The 
proposed design is more foolproof than the current safety 
trichotomy.
A user could create a @trusted function to do something unsafe 
and do it properly, but also do something that he expects to be 
safe while it’s not (like, calling a @system function thinking 
that it’s @safe). In a @safe function with @trusted blocks it 
wouldn’t be possible to do something unsafe if you don’t mean it.

To sum up, @trusted blocks will make it easier to write safe 
code, read safe code and learn the language. This proposal aligns 
with the current direction of the language towards memory safety.

How much will this change break? Probably not that much, because 
@trusted functions are not common.

To upgrade your code, the proper thing to do would be to put only 
the unsafe parts inside @trusted blocks. It would require some 
manual work, but it’s not for naught, since it would re-enable 
compiler checks outside of @trusted blocks. You can even find 
some bugs your eye could miss!

Of course, you can also just put the whole function body inside a 
@trusted block and call it a day. Not great, but just as bad as 
the current @trusted attribute.

Additional thoughts.

Obviously, @trusted block must not introduce a new scope. If you 
need a new scope, just use double curly.

We could also allow applying @trusted to a single line:
@trusted unsafeFunction();
But I don’t think that’s a good idea, as it would make it just 
too easy to call unsafe functions from @safe code.

With only @safe and @system attributes left, it would make 
perfect sense to rename @system to @unsafe, as suggested by Manu. 
The only ones who will protest are those who use “I write @system 
code” as a pickup line.

It would be possible to write a tool that analyzes code safety 
and e.g. shows how many @trusted lines are in the dub package.

The @ should be probably dropped.

[1] 
https://forum.dlang.org/thread/blrglebkzhrilxkbprgh@forum.dlang.org


More information about the Digitalmars-d mailing list