Rust 2018: an early preview


#48

Is it intentional that absolute paths starting with a leading :: rather than crate:: no longer work?

For example:

struct Foo;

// works in 2015, not in 2018, can't find crate for Foo error
struct Bar(::Foo);

// works in 2018, not in 2015
struct Baz(crate::Foo);

#49

At least partially yes, though last I heard it’s still somewhat up in the air what will happen to :: in the 2018 edition. ::Foo will certainly not be idiomatic, will most likely be deprecated, and may either change meaning like what you see there or even be removed entirely.


#50

About pub(crate) vs crate: I think pub(crate) is better. It’s consistent with pub(super). Also, crate is just Rust’s equivalent to what other languages call package. It’d look weird to use package in that position, so I don’t see why using crate in that position should be any less weird. I’d prefer to stick with pub, pub(crate) and pub(super).


#51

While pub(crate) is currently not used often, in the future it will be - possibly even more often than pub.

To illustrate this, consider the following hypothetical channel implemented as a library:

pub struct Channel<T> { inner: queue::Queue<T> }

impl<T> Channel<T> {
    pub fn send(&self, msg: T) { self.inner.push(msg); }
    pub fn recv(&self) -> Option<T> { self.inner.pop() }
}

mod queue {
    pub struct Queue<T> { ... }

    impl<T> Queue<T> {
        pub fn push(&self, value: T) { ... }
        pub fn pop(&self) -> Option<T> { ... }
    }
}

Everything in module queue is declared pub, but it doesn’t have to be. We could’ve used pub(crate) as well. More importantly, note that structure Queue and its methods are not really public - they’re only visible to the parent module.

Now, this example is pretty simple, but when reading a big codebase with a lot of modules, structs, and functions, it can be frustratingly difficult to figure out whether pub struct Queue is visible to other crates or not. Therefore we’ve decided to have two kinds of pub: visible to other crates (pub) and visible within this crate (crate).

To improve code readability, we should’ve ideally written the previous example like this:

pub struct Channel<T> { inner: queue::Queue<T> }

impl<T> Channel<T> {
    pub fn send(&self, msg: T) { self.inner.push(msg); }
    pub fn recv(&self) -> Option<T> { self.inner.pop() }
}

mod queue {
    pub(crate) struct Queue<T> { ... }

    impl<T> Queue<T> {
        pub(crate) fn push(&self, value: T) { ... }
        pub(crate) fn pop(&self) -> Option<T> { ... }
    }
}

This is much better. Now it’s perfectly clear that Queue is only an implementation detail of this crate, and definitely not something visible to other crates. Maybe this example is not particularly convincing because it’s very small, but I’ve personally found the overuse of pub to be a serious problem.

Anyways, nobody is going to write pub(crate) in this situation because pub is simply easier to type. So we want to make it easy to type code that will be more readable, hence why we’re introducing the crate modifier. It should be used very liberally.

As far as I’m concerned, crate as a visibility modifier cannot come soon enough. :slight_smile:


#52

This begs the question of whether pub(super) is consistent with pub. pub(super) limits visibility to the parent module in the same crate. Thus, pub(super) isn’t public; it’s more like java’s package-private, C#'s internal, or C++'s “not listed in a header file”. It’s more consistent for pub(super) to be crate(super).

FWIW, crate wouldn’t be my first choice for this-- I’d prefer local (as noted in the RFC), or appropriating internal from C#.


#53

pub(super) means it is made public to the parent module. I think the keyword fits quite well.


#54

For a fair comparison on pub(crate) versus pub usage, turn on #![warn(unreachable_pub)] and fix all of those warnings to be pub(crate).

I am on the side of crate fn being a good thing – a fn for this crate – because it creates a much clearer distinction between pub-to-the-world and pub-internally.


#55
crate mod ...

Means I’m declaring a module that has crate-level visibility. To me that reads clearer than pub(crate). Not to dismiss your’s or anyone else’s opinion on this though, I’m just pointing out that saying whether pub(crate) mod or crate mod reads better or makes more sense entirely depends on who you ask and how they read it.


#56

@gbutler You’re probably right in that once we get used to it, it will probably be quite clear. It’s even likely that after a while we’ll prefer crate because it’s easier to write xD Also, I’ve now noticed that my comparison to pub(super) has a flaw: pub(super) visibility is not nearly as important as crate or pub visibility because visibility limitations within a crate are less significant by comparison.


#57

I always considered pub(crate) as a temporary hack to fit into the current syntax without breaking anything. The „parameters“ or whatever it is seems clumsy. Any reason why super is not a separate visibility modifier as well?


Visibility modifiers: pub(super) vs. super
#58

+ to the question.

Quick search in the rust repo (without tests and docs) - pub(crate) - 118 results, pub(super) - 170 results.

I’ve gathered some statistics about distribution of “sufficient visibilities” in the original RFC - https://github.com/rust-lang/rfcs/pull/1422#issuecomment-174656923 and pub(super) was more common too.

(I addition, pub(crate) in the root module is useless and can be removed, but pub(super) in root module is invalid, so use of pub(super) by default even in cases when super is equivalent to crate prevents this little mistake.)


Visibility modifiers: pub(super) vs. super
#59

@vorner @petrochenkov @MajorBreakfast @gbutler @illustrious-you @CAD97 @stjepang

I have started a new thread for this discussion about super and pub(super) to keep this thread more focused on the preview: Visibility modifiers: pub(super) vs. super.


#60

I’m curious if stabilizing crate visibility later would be an option…


#61

I like the changes to macros in module system, as someone already wrote above this will syntax will be pretty, (both foo and bar extern crates)

use foo::foo!;
use bar::{bar!, zoo!};

One thing may be inconsistent (in my opinion) is that for submodules we still need to use #[macro_use], it will be better to extents syntax for external crates for submodules.

In our crate, we have a zoo submodule with #[macro_export], the above syntax is consistent.

use zoo::{zoo!, copy!};

This even leads to re-exports for libs, being able to re-export their macros in a simplified prelude.

pub mod prelude {
    pub use macros::{scr!, link!};
    /* ... */
}

#62

I like most stuff, but I’m not a fan of the anti-pub lint in binary programs. It sounds nicer to me to make my structs “public”, even if public is restricted to the local crate in this case. I always thought of crate as something that is used only in libraries when pub means super public, and something that wouldn’t be used that much. But now it seems like crate has completely replaced pub in binary programs and I’m not a fan of that. It’s longer, and if I ever want to separate stuff into a library then that’s going to be more difficult, renaming all the crates to pub.


#63

Maybe pub can be made to mean pub(crate) and pub(api) (or pub(extern)) to mean pub?

The more public an item is, the longer access specifier it should have.


#64

I make stuff pub in binaries sometimes so I can generate docs for them, for my own use.


#65

Slightly off-topic in here, but if it’s for your own use, you can pass --document-private-items to rustdoc.


#66

Actually, what I tend to do these days is put whatever the logic of my command line app is in a library (lib.rs), and then add a (bin/cmd.rs) with just the args parsing, logger setup etc.


#67

Links in the OP don’t work.