Visibility modifiers: pub(super) vs. super


#1

So… Should we allow super in addition to crate as visibility modifiers?

Discuss :slight_smile:


Rust 2018: an early preview
#2

In practice, I think it exceedingly unlikely that it would matter if every single pub(super) became crate; the additional visibility wouldn’t matter in practice, because either way it remains limited to the current crate.


#3

While when I saw it for the first time I was a bit confused by the syntax, pub(arg) has never really posed any issues for me in practice, quite the reverse actually. In addition, the syntax makes it immediately apparent to me what “level of publicness” is being used, so I don’t have any problems with it at all.
Of course I’m just one voice in a rather large (and growing) crowd.


#4

Honestly, I find the whole pub(modifier) somewhat odd but not bad or important enough to change. I personally think pub mod ... and crate mod ... read better, but, there is nothing particularly wrong with pub(modifier) mod .... either. If pressed, and we were voting on which syntax to use in the beginning, I’d vote for the <visibiilty specifier> mod ... rather than the pub (<visibility modifier>) mod .... syntax. But, since it was implemented the way it was, I don’t really think spending any significant effort on changing it is worth much. The “Juice isn’t worth the squeeze” so to speak.

I find a lot of these syntax arguments to be similar. Argue about it before it is implemented. Once it is implemented, changing it, or bike-shedding on it again is not very valuable.

EDIT: The “weirdness” of pub(modifier) mod ... also depends on how one reads pub. Does pub mean public or publish? If the latter it can be read as:

pub : “publish default/globally”

pub(crate) : “publish to crate”

pub(super): “publish to the super module”

etc. So, if one reads pub as “publish” then pub(modifier) makes more sense with the default modifier being “public/global”. Maybe if we just started teaching/documenting/etc. that pub mean “publish” rather than “public” and kept things as they are? But, again, I don’t think there is anything particularly wrong with the status quo.

EDIT 2: The more I think about it, it is the lack of symmetry/consistency that feels a little wrong (but again, not much of an issue). If we wanted to make it more consistent have it be (and teach/document it as such):

  • pub means “publish/publicize”
  • lack of visibility specifier is short-hand for pub(priv) pub(self)
  • pub by itself is actually short-hand for pub(extern)

The “real/full/explicit” visibility specifiers are:

  • pub(extern) - publicize external to crate (globally/public) - alias/short-hand pub
  • pub(crate) - publicize to the entire crate
  • pub(super) - publicize to the super module
  • pub(self) - publicize within the module the struct etc exists (i.e. private)
  • pub(priv) - do not publicize (private) - alias/short-hand, lack of specifier/blank
  • pub(in <module::path>) - publicize within module::path

So, if someone wanted to use explicit visibility modifiers always, they could at their option use pub(priv) pub(self) instead of lack of a visibility modifier to mean private and pub(extern) instead of just pub to mean public globally/externally to the crate. In addition, sticking with this and teaching it this way, would make it easier to add additional visibility modifiers in the future (for example, pub(friend) (see for example, Revisit Orphan Rules), without disrupting keyword reservations or the grammar. If this is the road that would be pursued, then, getting rid of crate mod ... as an alias for pub(crate) mod .... should probably happen.

EDIT 3: Re-reading, https://doc.rust-lang.org/reference/visibility-and-privacy.html, it becomes clear to me that what I’ve expressed above is kind of the intent of pub (that it should be be read as “publish/publicize” rather than “public”), but, the documentation as written seems to push reading it as “public” instead based on the wording chosen in the explanation. The more I look at this, the more I just think it is a documentation and symmetry problem. Update the documentation to make clear that pub means “publish/publicize” and add the pub(extern) and explanation of that, for symmetry, and get rid of crate ... instead of pub(crate) ... (so as not to break symmetry) and all should be good.

So, stop saying “public vs private” when talking about “pub”, and instead talk about “publicizing/publishing externally/globally” vs “publicizing to crate” vs “publicizing to super module” etc. If that is the language used in documentation, etc. everything should be much more clear.


#5

This also explains why there’s no priv keyword or anything to that effect, aside from the fact that it would be redundant of course.


#6

I feel that’s similar to how changing every private item to crate would still work. The crate API would stay the same, and it would only be reachable from inside the crate.

However, as I argued during the whole visibility discussion, in-crate visibility control is regarded as very useful by some people. There are invariants you want to be upheld across multiple modules or hierarchies, not just at the crate level.

Imagine if servo and related crates only had world-public and crate-public items, and couldn’t restrict visibility any further.


#7

I think there could be genuine confusion around how super in other languages refers to a super class or method. I sometimes forget this means something else:

impl Bar {
    fn foo(&self, arg: Baz) {
        super::foo(arg)
    }
}

I personally find it easiest to understand that modifiers to how pub an item is go inside pub(scope).


#8

I had always thought pub was Rust’s spartan take on public, but I think that the idea that pub is short for publish is a very enlightening way to think about it. It even reads well!

// publish the structure Foo, and publish in the module foo its field x, an i32
pub struct Foo {
    pub(in crate::foo) x: i32,
}

#9

My question: Is that historically accurate, or is it an ex post facto rationalization?

(Not a rhetorical question; I actually have no idea whether this is true or not but want to find out…)


#10

I’m guessing ex post facto. pub/priv are almost certainly short for public/private.


#11

@gbutler - pub being shorthand for publicize is a great way to understand the mechanic.

I question, however, whether this formulation can stick. I doubt anyone will consider bare pub to mean publicize, when it’s effect the same as public in other languages. Though it is unlikely to be well received, I wonder whether redefining pub to mean publicize means it should always specify its target (for pragmatic reasons, this would be enforced by a warning lint).

Related


#12

After a few minutes of googling, it looks like the pub keyword was introduced by the very first RFC ever. None of the RFC text or discussion explicitly states what if anything the keyword is short for, but “public” appears several times and “publish” does not appear once, so it seems safe to assume “public” was the historical intent.

That aside, I do see the appeal of retroactively verbifying pub to either “publish” or “publicize”. I think I’m on the fence for now, and we should probably wait to see how crate and pub(in ...) usage shakes out once all the module system tweaks are stable for a while before bikeshedding this.


#13

My contention is that the usage of pub actually aligns with the notion of “publicize/publish” better than it does with “public” despite whatever historical intent that “pub” was short for “public”.


#14

Thinking about it, I feel like having the more unusual versions of pub @gbutler suggests (pub(extern) and pub(self)) seem like a weird idea at first, but I like to imagine that some larger projects might want to be explicit about how different items are published, to the point that there could be a pedantic clippy lint that can be switched on to forbid items without the full pub(..) form. I think it would be good to allow them for this purpose.


#15

To be clear, pub(self) already exists. The only one I’m suggesting to add is pub(extern) to complete the symmetry.

EDIT: Also, I said that pub(self) is equivalent to no-specifier (which is “private”). Re-reading the documentation here,https://doc.rust-lang.org/reference/visibility-and-privacy.html, seems to indicate that is not truly the case. It appears that pub(self) and no specifier (e.g. “private”) are slightly different. “Private” (no specifier) items are visible within the module they are declared AND any sub-modules (which kind of isn’t “private” really) whereas pub(self) items are ONLY visible within the module they are declared and are not visible within sub-modules (am I reading the docs right?). If that reading is correct, then I would also advocate adding the pub(priv) specifier for symmetry.

EDIT 2: Re-reading https://doc.rust-lang.org/reference/visibility-and-privacy.html again, I’m not 100% clear on whether pub(self) is supposed to be the equivalent of private (no specifier) or not. The language used in introducing the qualified specifiers seems to indicate that the qualified specifiers are all cases different from “public” (bare pub) and “private” (no specifier), but, the example and explanation shown for pub(self) does not show any particular restriction with respect to sub-modules. My reading is that the example just fails to make that clear, but, that the intent of pub(self) is to be restricted from visibility of sub-modules. Is that correct?


#16

I like to be as explicit as possible/required in the programs I write. I try to force myself into thinking how my future-self (after a year of not working with the code) would perceive this piece of software, and how long it would take me to get back on track in understanding and contributing to the code.

These sort of lints truly help me in that regard, and the small upfront cost of explicitly stating visibility modifiers on all objects, far outweighs the hunt for visibility differences a year from now when I get back to my code.

This of course depends on the size of your project, and the longevity the code of your project is expected to have, so it being optional is very important.