pub struct Script;
Expand description

Rust 1.29 and 1.30

Hello, I’m Chris Krycho, and this is New Rustacean: a show about the Rust Programming language and the people who use it. This is a news episode, for Rust 1.29 and 1.30.

Parity Technologies is here sponsoring another episode! Parity is advancing the state of the art in decentralized technology. Their flagship software is the Parity Ethereum client, but they’re also building cutting-edge tech in areas like WebAssembly and peer-to-peer networking. Their next big project is Polkadot, a platform leveraging blockchain tech for scaling and interop in decentralized systems. Parity uses Rust for its trifecta of safety, speed, and correctness—and they’re still hiring Rust developers! Check out their jobs at paritytech.io/jobs.

Thanks again to Parity for sponsoring the show!

Rust 1.29 Changes

Rust 1.29 was a relatively small release, because Rust 1.30 was and 1.31 is going to be really big releases. (If you’re wondering why there is a combined episode for these: that plus the burnout I talked about was enough for me to say pass.) But there were a couple things, so let’s talk about those before we dive into the larger changes around 1.30 and the 1.31 beta!

The biggest feature in 1.29 was the new cargo fix command in Cargo. cargo fix is a general-purpose tool to fix a certain set of compiler warnings for you. If you’ve ever looked at one of Rust’s extremely friendly and detailed compiler warnings and thought, “Okay, Rust, if you’re so smart and can tell me exactly what I should change here, why don’t you just fix that for me?” well, cargo fix is what you’ve been looking for! Of course, it’s important for this to be relatively conservative: the tool should only fix things where it’s 100% sure that the fix is correct. The new compiler API that drives this will expand over time as new sure-to-be-right fixes land, but for today it will only fix a few things.

cargo fix, being this general-purpose tool, is something people have dreamed about for a long time, but it landed when it did because we need it for the 2018 Edition release to be successful. And that takes us to a special flag you can pass to cargo fix: --edition. This checks your code against the current and next editions of the compiler, and helps you get the code into a state where it works with both. If it can safely update your code automatically for you, it will; otherwise, it’ll print warnings with instructions about how to proceed.

I mentioned cargo fix in an earlier episode when the first beta of the feature was released, but it’s now on stable Rust – and in my experience so far, it works really well! I converted my (now active again!) Lightning static site generator project using it and it was a piece of cake. Mind: that’s a whopping thousand lines of Rust so far, so it’s very, very small in the grand scheme of things. But! cargo fix just worked, and using it was a great experience.

Rust 1.29 also shipped a preview of the cargo clippy command. Clippy is a tool for linting your code – named after the old (silly, annoying) Clippy tool in Microsoft Office back in the 90s: the one that would put up prompts on your screen like “It looks like you’re composing a letter; would you like help with that?” In Rust, Clippy suggests best practices – things that aren’t at the level of compiler errors, but which are likely to make your code more maintainable over time. cargo clippy brings this to a first-class integration with the language. It’s still in preview, but you can check it out now.

There were also some small stabilizations in the standard library and a few new targets added to the compiler here – as always, you should look at the full release notes to see all the details of the release, and as always they’re linked in the show notes – but most of the focus was on the pieces landing in 1.30 and 1.31, so let’s talk about those!

Rust 1.30 Changes

Rust 1.30 dropped a buuuunch of changes on us. The biggest two are stabilization of procedural macros and new features for how paths are handled for crates and items within them.

Procedural Macros

Procedural macro stabilization has been a long-desired feature for Rust, because procedural macros let you do some really amazing compile-time code generation. But they’ve been limited to nightly for a long, long time. Back in the Rust 1.15 release, a subset of procedural macros were stabilized. (I talked about those a bit in the Crates You Should Know: Serde and Traits Deep Dive Part I episodes, both linked in the show notes.) However, those were only part of the story… and the rest of the story landed in Rust 1.30. This brings two new kinds of macros we can write:

  1. We now have the ability to write our own attributes. Recall that custom derive macros were limited to macros which could run in the context of the compiler’s existing #[derive] macro. That limitation is now lifted. This opens the door for things like Rocket’s routing attribute macros to work on stable.

  2. We can write function-style macros, which are like macros-by-example (which we covered a bit back in episode 10), except that they have the full power of procedural macros: that is, full access to all the tokens within the body of the invocation and the ability to generate all the desired code we want. Remember that macros by example don’t have access to the compiler, they’re just smart syntax substitutions. Procedural macros get to actually run the compiler themselves for code-gen purposes, and are thus strictly (and profoundly) more powerful.

These two features being stabilized mean a lot of things that previously only worked on nightly are available for everyone to use on stable Rust today. That’s a big win!

Finally, one of my longest standing annoyances got fixed: you can now reference macros the same way you reference… everything else. Historically you had to write the #[macro_use] attribute on the extern crate reference for the crate where you wanted to pull in a macro… but you didn’t specify the name of the macro there! It’s always been a bit of a weird outlier. Now, you use macros just like you do anything else. So, for example, to pull in Diesel’s not_none! macro, you would just write use diesel::not_none;… and drop the #[macro_use] entirely. This is a really nice win for consistency throughout the language.

Paths

That also takes us right into the other huge (but still backwards compatible!) change in Rust 1.30: module paths got a really nice set of improvements.

It’s no secret that understanding Rust’s module path system has been a serious learning hurdle for a lot of people. There are a lot of reasons for this, including just the ways it is different from other systems—but the biggest is that the module system we shipped with Rust 1.0 works one way in the root of the main module (whether that’s lib.rs or main.rs) but does not work that way everywhere else. Those differences (and many other confusions) flowed out of the way that the actual rules for paths worked to make them items like everything else—the kind of thing that was super elegant but also, in practice, super confusing.

The difference had to do with what extern crate did in the context of the crate root. If you wrote extern crate regex, then in your main function or somewhere in that lib.rs file, you could reference anything on regex directly—for example, by writing regex::Regex::new(). However, in the rest of your crate, even just in a nested module written inline in that same file, you explicitly had to use regex; to make that invocation work, because the namespace—the lookup context—had changed from the root.

This confused basically everyone. So Rust 1.30 brings a big fix (and the 2018 Edition yet more fixes; more on that in the future). Here’s how the fix works:

  1. You don’t need extern crate anymore, at all, ever. You can simply refer to crates’ paths by their crate name and the Rust compiler does the right thing and looks them up for you. So you can use serde::Deserialize; without needing to write extern crate serde; first.

  2. You can name things from your crate root with the new use of the crate keyword. It used to be that paths after the use keyword started from the root, but paths in an item context (like a method name) used the local path instead. You may have seen a reference with ::some_path before; this was the old, rather ugly way to get back to the crate root to start a path. That style has been replaced by the new, much clearer crate::some_path syntax, and as a result paths can now work consistently wherever you use them, so long as you include the crate keyword where you should.

Running the aforementioned cargo fix --edition will do this for you, as an aside: because Rust 2018 requires the new style (whereas Rust 2015 simply supports it).

In my experience, switching to this new system was incredibly easy. It was a case of things just working the way they felt like they always should have worked.

Grab bag

Finally for 1.31, there were a number of other, smaller features worth mentioning in this release:

  • There’s now syntax to let you use identifiers as keywords: you write r# in front the keyword. This is not something you’ll normally see often… unless you’re using one of the new keywords from Rust 2018: async and await for use with Futures as they standardize, and try to replace the current do catch blocks, which are sugar for special handling around things like Result which implement the Try trait. You may have code in Rust 2015 that uses try, or async or await as a name for something, because they weren’t previously keywords. You can keep those in Rust 2018 code, by writing them (for example) r#async. (I believe cargo fix --edition will handle this for you.)

  • You can now build no_std applications. Turns out that you’ve been able to write no_std libraries for a long time (since Rust 1.6 three years ago!), but not applications. You had to call no_std libraries from… not Rust. There’s now support for telling the system how to implement panics when the standard library doesn’t exist, via a new compiler attribute, #[panic_handler] which you apply to a function which can implement application behavior for panics. This is a big deal for contexts like embedded software or WebAssembly!

  • Cargo got a progress bar! It tells you roughly how far along in the process of doing a build you are. Just a nice little detail.

  • There are a bunch of handy little convenience values for IP address lookups: Ipv4Addr::LOCALHOST and Ipv6::LOCALHOST, for example. These are tiny, but they’re handy for making sure you don’t typo something where it matters!

And there are more; I’ve linked directly to the full release notes for 1.30 in the show notes as well.

Rust 1.31 – The Rust 2018 Edition Beta!

The other really big news is that the Rust 2018 edition is now in the beta channel. I’m not actually going to talk about the features, because I’m planning to do a series of episodes celebrating the release by digging into the biggest new features—they warrant that level of explanation. For today’s purposes, it’s enough to know in most cases that it’s nearly here, and you can and should test it out.

To install the beta, you can just do rustup update beta. Then you can either use it by doing cargo +beta build or cargo +beta run with your app, or by setting is as an override for a given project with the rustup override command.

I say this often, but I’ll reiterate as we’re coming into the home stretch: please test your projects and file any bugs that come up! This is a pretty big deal for Rust, and your bug reports or comments on unclear documentation (for the edition guide, for example) could be a really big deal for someone coming in to check out Rust either for the first time as the news hits, or again to see how it has changed.

Closing

That’s all for today. The episode on unsafe is coming together nicely, and I should be able to finish drafting it (and I might even be able to record it) over my Thanksgiving holiday! And of course I’m lining up a ton of coverage of the Rust 1.31 / 2018 Edition release.

Thanks, as always, to everyone who sponsors the show! This month’s $10-or-more sponsors included:

  • Scott Moeller
  • Oluseyi Sonaiya
  • Anthony Deschamps
  • Ramon Buckland
  • Paul Naranja
  • James Hagans II
  • Nick Stevens
  • Steffen Loan Sunde
  • Alexander Payne
  • Behnam Esfahbod
  • Graham Wihlidal
  • Nicolas Pochet
  • Martin Heuschober
  • Nick Gideo
  • Bryce Johnston
  • Daniel Collin
  • Rob Tsuk
  • Dan Abrams
  • John Rudnick
  • Matt Rudder
  • Jonathan Knapp
  • Jerome Froelick
  • Peter Tillemans
  • Chris Palmer
  • Nathan Sculli
  • Bryan Stitt
  • Joseph Marhee
  • Michael Mc Donnell
  • Ryan Osial
  • Jako Danar
  • Chip
  • Adam Green
  • Raph Levien
  • Daniel Mason

If you’d like to sponsor the show, you set up ongoing support at patreon.com/newrustacean, send a one-off at any of a number of other services listed at newrustacean.com, or get in touch directly. The website also has scripts and code samples for most of the teaching episodes as well as transcripts for many of the interviews, along with full show notes for every episode. You can find the notes for this episode at <newrustacean.com/show_notes/news/rust_1_29_1_30>.

If you’re enjoying New Rustacean, please help others find it – by telling them about it in person, by sharing it on social media, or by rating and reviewing the show in your favorite podcast directory.

The show is on Twitter @newrustacean, or you can follow me there @chriskrycho. Tweet me with news, topic ideas, etc! You can also respond in the threads on the Rust user forums, Reddit, or Hacker News, or—and this will always be my favorite—just send me an email at hello@newrustacean.com.

Until next time, happy coding!

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.