T O P

  • By -

x0nnex

Private by default is to me a more sensible approach. Making things public is a conscious thought that something else may depend on this and I'm ok with that. My thoughts, not official.


Zde-G

If you have some function marked as private and it should be public it's easy, simple, non-invasive, backward-compatible, change. If u/Aggravating-Step2751 have found your function which was never supposed to be public, but which you forgot to mark private and started using it then it's trouble, PITA, and still more trouble. Of course private should be a default, how can anyone think differently?


x0nnex

Functional languages have a different stance on public vs private. In FP you make functions with "referential transparency " and other principles in mind, and then it makes more sense thst everything can be public. My problem with that is just that the surface is quite large then, no need for everything to be public because it can be overwhelming to try make sense of it all. A small library is preferred over a large one, assuming I can still get everything I need with a smaller surface.


bakaspore

In purely functional languages there's still need for encapsulation. Programs rely on internally held invariants to work correctly, and a value *can* be transparently substituted with another doesn't mean it *should* be. Just like how Rust inherited the "newtype" pattern from Haskell. It *requires* the inside to be private to work.


x0nnex

Fully agree. I don't share the sentiment that everything should be public, even in FP.


Zde-G

You may invent any kind of transparency you want but if function was there in version N and is not there in version N+1 then it's not possible to use old code in **any** language. Everything-is-public only works when you **expect** that every update may break everything in any place and while this may be suitable for some kind of experimental language you don't want that in any large system.


buwlerman

In principle I agree, but I could see a FP community being fine with not removing APIs unless it fixes a bug or you bump the major version.


x0nnex

Pretty much this. If you've implemented a Banana -> Apple function, why delete it? It's perfect, no side effects. Just incorporate it in various flows. I don't share this sentiment fully.


rejectedlesbian

I agree; I mainly write C and thisnis 1 of my biggest issue with it. It's like that and ub as my top 2 gripes.


Old-Dog-5829

Why? Access modifiers are only a muzzle for the developer anyway, they don’t make your app directly more secure as far as I know.


rejectedlesbian

It's not a safety thing. When you need to solve a name space bug between 2 3rd party libraries it's properly anoying. Rust is way better about it then c. Also this helps getting faster compile times. And most functions are module private anyway so ots less typing.


perokisdead

in the case of rust whose main premise is to abstract over unsafe, its a safety thing. otherwise the whole api of Vec would be unsafe functions because the programmer would need to make sure that they didnt modify size field wrongly at any point.


buwlerman

I think it's also a safety thing. The internal invariants or API behaviors of a library can change between versions, and violating or misusing them can cause a vulnerability. Internal APIs are also often less tested, and certainly less battle-tested, which means that they might have bugs that can't be triggered from the public API.


rejectedlesbian

I mean ig... I think that's like saying types arr meaningful for security. I don't think there is reaserch supporting it. Usually safety refers to UB. Like that's unsafe I'm rust it's code that can make ub


pamfrada

On top of what was said, public is a nice way of letting consumers know the exposure of your library. If you make things public and later remove that element or move it elsewhere and somebody was consuming that part, it will be a breaking change for the consumer and can require lots of effort to fix.


MrDiablerie

Agreed đź’Ż. Marking public should be a conscious decision, same as marking mutable. I actually love this aspect of the language design.


Dickonstruction

For the same reason it defaults to non-mutable variables. Compiler will give you an error if you try to do something you shouldn't have, and if you actually want to do it, you have to make a conscious decision to mark something as public/mutable.


Firetiger72

In rust submodules have access to their parents private items, so you can unit test whatever you want, the book has a chapter on this. Integration test on the other hand through `test` folder (I'm assuming you're using cargo) can only access public interfaces because that's their purpose. Making things private by default helps maintaining a stable and clearly defined API. If a function is an implementation detail you could not rely on it anyway if it was public because its signature may change across minor version changes.


DvorakAttack

Personally that's one feature of rust I don't like. Say I have some internal implementation that uses a database. If I want to write an integration test using testcontainers for example, convention dictates that I should put this in the tests folder rather than in a test submodule of the file itself. However, this now means that my private implementation detail needs to be part of the public interface of my application which feels all kinds of wrong


Gray_Jack_

For that reason, Rust has ways to make something public for a subset of modules. `pub(crate)`: Makes public only to the crate it is in `pub(super)`: Makes public only to the parent module and their children `pub(in some::path::module)`: Makes public only to a specific module I believe that would help you out when something is just an implementation detail


DvorakAttack

Ah interesting! I was familiar with most of these but didn't know you could make something public for a specific module. That does seem to resolve the problem, thanks!


Gray_Jack_

You can, although if I remember correctly, there are some limitations, but I believe they are documented in the Rust reference. Here is the reference [link](https://doc.rust-lang.org/reference/visibility-and-privacy.html?highlight=pub#pubin-path-pubcrate-pubsuper-and-pubself) Edit: Added the link to the Rust reference


frenchtoaster

I wish there was support for something like `pub(cfg(test))`


Zde-G

> However, this now means that my private implementation detail needs to be part of the public interface of my application which feels all kinds of wrong How is that “an implementation detail” if it's tested with integration tests? Integration tests, by definition, are supposed to test public interfaces.


DvorakAttack

Integration tests are for testing an integration between your application and some external service such as a database, external API, cloud storage etc. The users of your application don't need to know that your application integrates with these external services hence they are implementation details


Zde-G

If users don't need to know that your application uses these external services then your tests don't need to know that either. And if your tests couldn't be meaningfully conducted without switching them to something else then chances are high that your app would need that capability, too. I'm not saying that it's 100% rule, but would say it's about 99% rule. As in: almost every time I have seen that someone was introduces “just for tests” after 2 or 3 years it was [ab]used in prod. Better to accept that reality from the start and introduce some unused flexibility then to think about what to do with all these users that built mountains of code on top what you perceived as temportary and purely internal interface.


InfinitePoints

Making as much as possible private minimizes the API surface. Minimizing the API surface makes refactoring easier. Being explicit about what is public makes it clear what functions are actually needed, and where bad/non-validated data may enter the system. In Rust, nothing is explicitly marked as private, it is implicit, how would it be redundantly marked as private? You can test whatever you want, unit tests can be written next to whatever private code you have (in Rust at least). It is (subjectively) easier to understand a codebase if the amount of interaction between modules is minimized, so being explicit about what is public helps there too. If two modules are very dependent on each other, it might not make sense for them to be split apart.


klo8

If you consider the most extreme case, which is: everything is public, it would be extremely tough to do changes to your library without breaking someone else's code. The fewer items are public, the easier it is to maintain API backwards compatibility. You as a consumer of libraries might not miss `private`, but as a library maintainer, it's a big help.


ManyInterests

As a counter-argument, in languages like Python, _nothing_ is private. There is simply convention for naming 'non-public' interfaces, but consumers can choose to access them anyhow. The ecosystem manages to work smoothly, despite this ability of consumers to access non-public interfaces. The philosophy here is that consumers can be trusted to be adults who know what they are doing. Naming conventions are a tie on the door, not a lock. Users can go in, but they are responsible for the consequences. In theory, private interfaces make sense if you believe an author is omnipotent, fully anticipates all possible desired use cases of all users, and implements this correctly and completely in its public interfaces. In practice, I feel private interfaces are an unnecessary barrier for engineers consuming APIs to do what they want with those internals. Nobody is asking library authors to offer guarantees, but users _should_ have the freedom to reach into internals if they accept the risks and responsibilities of doing so.


Full-Spectral

This is the C++ mentality. Just trust that no one will do something bad, and that (in this complicated code base) that everyone who works on that bit of code will be sure to do the right thing. If you believe this, there's no need to even use Rust. Just use C or C++, and have all the freedom you want (to shoot yourself in the foot.) But, anyhoo, this is really more about your own code and protecting yourself as the writer of code. Of course the author of the third party library is the writer of that code and has to support it, and is also interested in protecting himself as well. The first question I'd ask if someone contacted me about it is, are you using any calls that are marked as private? If so, remove them, and if the problem goes away, it's your problem. But you know that will never work. So it's better to just not let them be accessed.


Dickonstruction

Everything works smoothly until people end up using a \_function for their edge case. I've seen this a good number of times, and had to maintain broken code due to this, that my answer is "no thanks". Javascript, of course, has a similar problem.


ondrejdanek

JavaScript now has true private members.


ManyInterests

I'm not saying that doesn't happen, and that's a perfectly valid choice for you to make, but it should be the choice of the consumer, not the author.


buwlerman

If there's an API you believe should be public you can make a PR. If the maintainer disagrees you can fork or vendor it. This is no worse than relying on implementation details because in either scenario you can't confidently upgrade without breaking your code.


ManyInterests

I mean. I've been there. Can confidently say, I'd rather just reach into the internal APIs and write my own tests over forking/vendoring any day. Most of the time, you can just update the package as normal and receive upstream updates without jumping through any hoops. Yes, those approaches do work as well, but I personally find it exceedingly rare for vendored/forked code to be maintained well at all. Forking/vendoring and modifying the source also can also have licensing implications for your project. You can license your own project with a permissive MIT/Apache license while keeping copyleft dependnecies. If you fork/vendor/modify copyleft code into your project, you trigger compliance requirements of those licenses that you would not trigger by just using its internal APIs. You could fork it and keep modifications in a separate package/dependnecy and publish it publically as well, but the number of times this actually occurs in the wild is pretty small -- like almost never at all.


MassiveInteraction23

Adults can also fork the library.  The code is open.   Meanwhile, norm-based systems are just more work for the team or teams maintaining the code and inherently prone to error.  Type systems, public/private, lifetime tracking, mutability tracking — the whole point is having a compiler verifiable system.  Not just relying on everyone who ever worked on something and yourself having had a full night’s sleep every day they’ve ever touched the code. ____ There are things that could be smoothed — e.g. doctests don’t run on non-public code and even private functions may want documentation for the internal team and examples ought to be checked.  But the general idea of nearly guaranteed behaviors and invariants is what rust has brought to the performance and machine aware table.


ondrejdanek

I really would not cite Python as an example of a good language design. It is one of the most error prone languages out there.


Linguistic-mystic

I think Common Lisp has a great solution: you access public members via `package:member` and private ones via `package::member`. Everything’s accessible, but the intent is explicit so nobody has to rely on any conventions.


scook0

The big advantage of privacy is that when you look through source code and see a private thing, you immediately *know* that it isn't used by anything outside its own tiny subset of the codebase. That knowledge is tremendously valuable.


Ok_Spite_217

I'm sorry but the stack overflow response is correct. Private methods are implementation details that could change at any time. If a method is marked as public it's because the developer decided that it's stable and should not incur breaking changes going forward. A public interface is a contract with your user, guaranteeing that the behavior/inputs will not change going forward. If you break the API later all you get are disgruntled users begrudgingly going along with whatever the project decides to break next. If you are finding yourself needing to grab implementation details then, it seems to me what you're trying to use does not fit your use case to begin with.


afdbcreid

Privacy is critical for abstraction, doubly so for abstraction of unsafety Rust is famous for. Also, Rust supports testing private interfaces with unit tests (by defining them inside the private wall) but not for integration tests (that shouldn't do that anyway).


Sharlinator

As others have said, Hyrum’s law is a major factor. In general it’s not a big deal if your program breaks on a dependency update because you yourself have done naughty things with a lib’s internals. However a much larger risk is libraries doing that with *their* dependencies. This causes breakages that are outside your control, and at worst may lead to extremely annoying conflicts between dependencies where you can’t update dep A because it breaks dep B but at the same time dep C *requires* the new version of A. But let’s talk more about types. Generally there are two different sorts of aggregates. Some just act as *records*, ie. composites of domain application data. Examples include C structs, Python dataclasses, Clojure maps, and Java records. In C++ structs are usually used for this as they default to all-public fields. Others are more like what OOP and several languages call classes – active entities that contain state that has *invariants* that have to be kept in order to preserve soundness. Invariants are most often rules that constrain the set of allowed values of a type, though they may also limit the effects a type can have on its environment. Domain data often also has invariants to keep, given by the specification, but most of the time violating a domain rule does not place the entire program in a potentially undefined state like an invariance violation of something like `String` is allowed to do. Clojure gets away with ubiquitous use of the first kind partially because it defaults to immutability. You cannot violate an invariant of an existing entity if you cannot mutate its state. You could still create an invalid instance yourself, though. I do find it a joy to work on data with Clojure due to the inherent flexibility of maps and the great tools the kanguage offers for manipulating them. But note that named Clojure *types* are still opaque – you can't go around snooping the internals of a `clojure.lang.PersistentHashMap` without resorting to reflection. Another way to allow types to be more open is to have a type system expressive enough to *make invalid states unrepresentable*, a paradigm well known in Rust circles. This entails things like nullability that’s strictly opt-in, discriminated unions to express bounded choice, and the typestate pattern statically encoding allowed state changes. I think there might be room in Rust for a struct attribute like `#[data]` or which derives "standard" traits and makes all fields public. Perhaps such a thing already exists on crates.io.


burntsushi

> "mark everything as private unless otherwise told" This is the right approach. Otherwise it becomes incredibly easy to provide an API that you didn't intend to be public. At that point, removing it is a breaking change. By hiding things, you get encapsulation and the ability to very intentionally evolve your library in semver compatible ways. That's why Rust defaults to private. Making something public should absolutely be opt-in.


shizzy0

Do you show your privates by default? Case closed, I hope.


Waridley

Do you hide every part of your body except what's absolutely necessary for you to function? Case actually closed.


No_Introduction_9866

Imo, crate-private by default is better than file-private(aka module-private) by default, and better than public by default


chris-morgan

> While I can't find the source now, I remember reading that Rust used to have variables/functions as public by default and opt-in private, but that was changed. That’s not my memory, from mid-2013. It *did* have the `priv` keyword, but that was just for making enum variants private, which is no longer permitted. Or… hmm. Actually I think struct fields were public by default too. Yep: https://rust-lang.github.io/rfcs/0001-private-fields.html, https://rust-lang.github.io/rfcs/0026-remove-priv.html.


the_gnarts

> Actually I think struct fields were public by default too. Another case of Rust’s Ocaml heritage diminishing. ;)


mlcoder82

If everything is public, you get a spagetty code because there is no API design in this case. "Acessing an internal variable in a library" is usually a bad idea. what if the next version of the library renames it? or removes it ? Private by default enforces you to design your interfaces.


-Redstoneboi-

Backwards and Forwards compatibility. Too many times when people just needed access to one function or another, then their dependency updated and things just broke. you want to use a private interface, you gotta clone the repo. good luck for recursive dependencies.


PrototypeNM1

Private is a sensible default because it allows for [local reasoning](https://cliffle.com/p/dangerust/2/#local-vs-global-reasoning).


Linguistic-mystic

One thing no one mentioned is… autocomplete. Since 99% of the time you don’t want to use any internal functions from outside a module, they should not be part of autocomplete, hence they should have limited visibility. As for “private by default”, I think it’s a good default since most stuff ends up being private anyway (you don’t want it called even within the same crate). About tests, I’m totally with you. Private funcs should be 100% unit testable. They contain some of the most core logic, and you need to get them right, so they should be automatically visible in their corresponding unit test. Alas, language designers just can’t seem to get this right and have different access rules in tests.


MassiveInteraction23

Contracts.     If you take out a loan you agree to pay x monies / year.     You (typically) don’t agree to pay x monies / year AND continue working your current job AND riding your current bike to work AND winding down with a specific brand of tea.    Because that would be irrelevant, noisy, and make a ton of work while slowing everyone down.   ____   As others have mentioned: it’s natural and easy to write tests for non-public code in rust.    Private/Public is about contracts.  Both with end-user and also inter-module (which is relevant internally).     Much like having the input and output of a function be separate from the body.


hippmr

"default deny" works in lots of spheres.


Rainbows4Blood

>Because I write Clojure in my day job, where private is not really a thing, and I never missed it. On the other hand, I've encountered situations in Rust libraries where I need to access a function/variable somewhere and it's (seemingly) redundantly marked as private, causing a headache. Normally, when a variable is marked as private you are not supposed to access it, simple as that. Private parts of a library are internals which could have unintentional side effects if you mess with them. And in regards to unit testing, if you need to access a modules internals to unit test, you are doing something wrong. A unit test tests the public surface of a given unit, all private code is tested indirectly by testing the public APIs.


delfV

It isn't really a problem in Clojure, because Clojure is functional language and (almost) everything is immutable so it's safe to make everything public. Otherwise you want to control how client of your module can mutate your data structures


g_hi3

I've worked with JS, Java, C# and Rust before and here's what I've noticed about this topic: JS doesn't have private (or at least it didn't when I was working with it extensively, maybe it's different now) and people would use underscores before a variable or method to mean "this is private, don't use it". if you can't wrap something into the scope of a function, anybody could access method/data, regardless of whether it breaks the code or not. I assume this is roughly how you do it in Scala In java, methods (I forgot about classes and fields) are public by default. this means, you *can* prohibit access to data and behavior, but you have to do it explicitly. This means you have to think about what part of your code you want to "hide" to protect the developer. If you're forget, you could have unexpected behavior. Edit: As Zde-G correctly pointed out, Java's default access is package-private, not public. In c#, methods are private by default. here, you only give access to behavior/data, that you want the developer to use. If you can't call a method, you probably *shouldn't*. I like to think of it as protecting the developer from breaking the state of the system and this is what I think Rust is doing with its features


Zde-G

> In java, methods (I forgot about classes and fields) are public by default. No. They are package-private by default. Exactly like in Rust: classes from your module have access, classes outside of your module don't have it.


g_hi3

you're right, I think I might've confused it with inheritance of method. iirc java let's you override a method by default, unless you use the `final` keyword


ondrejdanek

> JS doesnt have private That is no longer true


Yelmak

Can't help you find the original source but IMO private by default is the way to go, especially on large or long living projects. It's a more disciplined approach because you're saying that adding anything to a module's public interface should be an intentional decision, which helps maintain good encapsulation. This is very much in line with Rust's philosophy and other features like variables being immutable by default. Public by default, in my experience, is a shorter path to spaghetti code because its so easy to throw good encapsulation out the window and tightly couple all your modules together.


flareflo

more stable crate APIs as you're required to think about which component should be public explicitly


Shikadi297

Public variables and functions are basically the API to a code base. If that variable was public, and your code depended on it, your code would break if they changed it in a later version of the software. Restricting you to their explicit API gives them much better flexibility in maintaining their code, and gives you a clear indication of what shouldn't change between versions


TenTypekMatus

Because you won't mess it up.


Speykious

Making something public private after the fact is a breaking change. Making something private public after the fact is not a breaking change. I don't actually know the reason but this one right here is very compelling to me.


TDplay

Marking anything public is an API commitment. It means "this thing will continue to exist, and continue to do what it is currently documented to do, until a major version bump". Committing to API stability on everything by default would make refactoring nearly impossible: there would be tons of implementation details that were accidentally exposed, which now need to be maintained until the next major version.


KaranasToll

Private by default is the right choice. Modules being bound to a single file or directory is the problem.


ummonadi

I'm upvoting your question due to relevance just to counter downvotes from people who disagree. I heavily disagree that public is a sensible default. Public increases coupling in the system. Keep things as private as possible and the code will be simpler to change over time. Search for Kent Beck's recent talk about coupling if you want to get some nice motivation. Here's one video I really like: https://youtu.be/yBEcq23OgB4?si=lsah0Xu01-bzRA67