DEV Community

Cover image for Rust got 'pub' wrong
Lex Nwimue P.
Lex Nwimue P.

Posted on

Rust got 'pub' wrong

The pub keyword in Rust is used as an access modifier to make items—such as functions, structs, enums, traits, and modules—accessible outside of their defining module. In that sense, it behaves similarly to public in many other languages.

However, one major difference—especially noticeable if you’re coming from JavaScript—is the default behaviour.

In JS/TS, methods on a class are public by default (assuming you don’t explicitly mark them otherwise using # or the private keyword). The underlying assumption is that most class methods should be accessible, so there’s no need to constantly annotate method signatures with a visibility keyword. You could call this convention over configuration.

Rust could have borrowed this idea and arguably become even closer to perfection than it already is. But it didn’t. Instead, Rust went in the opposite direction: private by default, public by explicit declaration.

The result is that you often end up with code that looks like this:

Rust pub keyword litter

See it? A literal littering of pubs.

If the default behaviour were reversed, we might end up with cleaner-looking code—similar to how Rust handles the mut keyword. Variables are immutable by default, and when you explicitly add mut, it immediately signals to the reader that this value is going to change somewhere later. That signal is psychological as much as it is technical.

Right now, pub doesn’t quite have that same signalling power because it appears everywhere. It becomes visual noise. If, instead, Rust had opted for public-by-default, then explicitly marking things as private could have had the same effect that mut has today. Seeing a private keyword would immediately tell the reader: “This is intentionally hidden; don’t depend on it.”

In other words, the code would read more like:

“Everything here is meant to be accessible—except these specific things.”

Rather than:

“We want this, and this, and this, and this… to be public.”

Important Clarification (Where Rust Actually Differs from JS)

One thing worth correcting is why this comparison feels slightly off. Rust’s visibility is module-based, not class-based. In JavaScript (and many OOP languages), visibility is scoped primarily to classes. In Rust, visibility is scoped to modules, which act as explicit API boundaries. This means Rust isn’t just protecting fields and methods—it’s protecting entire namespaces and architectural layers.

So the choice of “private by default” isn’t just stylistic. It strongly encourages:

  • deliberate API design
  • minimal public surface area
  • fewer accidental breaking changes
  • clearer ownership of invariants

This is especially important in a language designed for large, long-lived systems and libraries.

So… is Rust wrong here?

Probably not.

But is the ergonomics debate valid?

Absolutely.

Rust trades verbosity for correctness and stability, and while that choice makes sense from a language-design perspective, it does come at a readability cost—especially for people coming from JS/TS, where public APIs are often implied rather than declared.

Whether that trade-off is worth it depends on what you value more:

  • visual cleanliness
  • or uncompromising explicitness

Rust very clearly chose the latter.

What do you think?

Top comments (0)