Is public by default an unsafe default?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu May 2 00:30:44 UTC 2024


On Wednesday, May 1, 2024 5:52:30 AM MDT NotYouAgain via Digitalmars-d wrote:
> No doubt this will create controversy too.. why.. I don't know...
> but anyway....
>
> Declarations within a D module (other than import declarations)
> are public by default.
> This includes public by default in a class too btw.
>
> Programmers from many of the other major languages will have to
> deal with this surprise (in addition to the surprise of how
> private has taken on a completely new meaning in D).
>
> Not discussing private here! We all know what will happen if we
> do. So don't!
>
> Question 1 - is 'public by default' a sensible default for a
> language that is aiming to be safe by default?
>
> Here's an interesting article relating to Kotlin, where I believe
> public is also the default. It kinda sets the stage....
>
> https://discuss.kotlinlang.org/t/kotlins-default-visibility-should-be-intern
> al/1400
>
> Question 2 - if public is not a sensible default for a language
> that is aiming to be safe by default, is it too late to change it?
>
> Question 3 - if a change was made, what would become the new
> default?
>
> Please be respectful in your comments, if you have anything to
> say.

For the most part, public and private really don't have anything to do with
@safe or memory safety. The primary exception would be when an @safe /
@trusted public API is being presented, but the internals are doing @system
stuff that has to be @trusted.

In such cases, if symbols are accidentally left public rather than private,
then @trusted code could be making assumptions that are not valid if any
external code accesses the symbols which were supposed to be private. But
even then, anything @system which is done with those private symbols will be
treated as @system, so the @safety system will prevent you from doing
anything that isn't memory safe with those symbols. It's just that if
@trusted functions make assumptions about the state of the symbols which are
not necessarily valid if external code has access to those members, then
@trusted would be treating stuff as @safe when it shouldn't, which could
lead to memory safety problems.

So, in a case such as that, having a symbol accidentally marked as public
could create @safety issues, but for the vast majority of code, it won't,
and so @safety and symbol visibility are largely orthogonal concerns (just
not 100% orthogonal).

Rather, I'd say that the bigger problem with public by default is simply
that it makes it easier to have symbols that were supposed to be private end
up in your public API. Then you can end up in a situation where something
that you thought was private is being used by external code somewhere, and
when you make a change that should only affect your code's internals, it
could break user code.

So, there's definitely an argument to be had that private by default would
be better, because then you have a lower risk of accidentally having symbols
be part of your public API when you didn't intend for them to be. However,
at the same time, it's quite trivial to just slap

private:

at the top of the module if that's a concern. And there are a number of
folks who would argue that all symbols should be explicitly marked with
public or private anyway (which I'm not a big fan of, but it does work
better with maintainability and code reviews when the attributes affecting a
symbol are directly on the symbol rather than somewhere else in the file).

Also, it's far more problematic to use

public:

because it will also affect imports, and you don't want to accidentally make
imports public. So, having private as the default could lead to more issues
with imports, because folks who wanted public on most of their symbols but
didn't want to mark them individually would likely be inclined to put

public:

at the top of their module and would then likely run into problems as a
result, whereas slapping

private:

at the top of the module isn't error-prone in the same way.

In many respects, I am inclined to think that it would be better for private
to be the default (at least as long as public: is changed to not affect
imports), but at the same time, in my experience, it's pretty rare that
symbols end up being public when they weren't supposed to be, and even when
they do, since they're undocumented, it's usually only a problem if someone
goes spelunking in the code and starts using undocumented stuff that's
public.

Ultimately both public by default and private by default work just fine, and
the issues that you get with one or the other usually aren't a big deal. So,
I don't think that it's all that big a deal which one a language goes with.

And if we wanted to be particularly concerned about it, then arguably, the
correct solution would be to just require that visibility attributes be
directly on the symbols to fix the problem, which would be annoying in some
cases, but mass-applying attributes often has a tendency to cause problems,
and public: in particular can be pretty bad because of how it affects
imports.

- Jonathan M Davis





More information about the Digitalmars-d mailing list