Rust 2018: an early preview


Problem here is what “a while” is. This would work if we were doing a 2021 edition (vs. 2018 edition), where new idioms between the 2 editions would be documented, and even that should really only be about those which happen to also break compatibility with the 2018 edition.

The thing about idioms is they change frequently, such that in 2024, even 2015 edition idioms will have changed (supposing people would remain with such an old edition, which will, according to plan, still be a supported setup).


(The plan is to make a new edition every few years, so it is very possible that we’ll have a Rust 2021 in addition to 2018 and 2015.)

@tshepang I think we’re making the same point. We give the same “edition” name to two things: “hey look, these feature are new since the last couple of years” v.s. “you can opt into some incompatibility and get this subset of new features in exchange”. The new features are not the same in both cases, so not having separate terms to talk about them can be confusing.


The in-band lifetime:

fn two_args(foo: &Foo, bar: &'bar Bar) -> &'bar Baz

Is easier to read as:

fn two_args2(foo: &Foo, bar: &Bar@bar) -> &Baz@bar

Or something like:

fn two_args3(foo: &Foo, bar: &Bar::bar) -> &Baz::bar
fn two_args4(foo: &Foo, bar: &Bar:bar) -> &Baz:bar

Maybe in-band generics:

fn other_args<'a, T:'a> (v: &'a T, size: int) -> T:'a


fn other_args(T: type@a, v: &T@a, size: int) -> T@a


Yeah, am aware… sorry that it wasn’t clear.


Really digging the new extern crate semantics!

With #![warn(rust_2018_idioms)], though, I’m getting some weird warnings about extern crate being unidiomatic, even though I have no actual extern crate lines anywhere. They all point to #[derive()]'s for serde::Serialize and serde::Deserialize as far as I’ve seen - is this because there’s an extern crate being generated as a part of the custom derive’s codegen?

I also managed somehow to get the same warning to point to the first line of my, which was just a comment, though I haven’t been able to reproduce it.




I’ll add my voice here to the list of people who think simplifying “pub(crate)” is an anti-feature. It’s not used often enough IMO to need shortening, and it overlaps with too many other constructs.


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);


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.


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).


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:


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#.


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


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.

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.


@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.


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

+ 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 - 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

@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.


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