@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