Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.31 announcement #295

Closed
wants to merge 16 commits into from
338 changes: 338 additions & 0 deletions _posts/2018-12-06.Rust-1.31.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
---
layout: post
title: "Announcing Rust 1.31"
author: The Rust Core Team
---

The Rust team is happy to announce a new version of Rust, 1.31.0. Rust is a
systems programming language focused on safety, speed, and concurrency.

If you have a previous version of Rust installed via rustup, getting Rust
1.31.0 is as easy as:

```bash
$ rustup update stable
```

If you don't have it already, you can [get `rustup`][install] from the
appropriate page on our website, and check out the [detailed release notes for
1.31.0][notes] on GitHub.

[install]: https://www.rust-lang.org/install.html
[notes]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1310-2018-12-06

## What's in 1.31.0 stable

Rust 1.31 may be the most exciting release since Rust 1.0! Included in this release is the
first iteration of "Rust 2018," but there's more than just that! This is going to be a long
post, so here's a table of contents:

* [Rust 2018](#rust-2018)
* [Non-lexical lifetimes](#non-lexical-lifetimes)
* [Module system changes](#module-system-changes)
* [More lifetime elision rules](#more-lifetime-elision-rules)
* [`const fn`](#const-fn)
* [Tool Lints](#tool-lints)
* [Library stabilizations](#library-stabilizations)
* [Cargo features](#cargo-features)
* [Contributors](#contributors-to-1310)

### Rust 2018

We wrote about Rust 2018 [first in
March](https://blog.rust-lang.org/2018/03/12/roadmap.html), [and then in
July](https://blog.rust-lang.org/2018/07/27/what-is-rust-2018.html).
For some more background about the *why* of Rust 2018, please go read those
posts; there's a lot to cover in the release announcement, and so we're going
to focus on the *what* here.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: link to Lin's post here, for a graphic explainer of what Rust 2018 is all about.


Let's create a new project with Cargo:

```console
$ cargo new foo
```

Here's the contents of `Cargo.toml`:

```toml
[package]
name = "foo"
version = "0.1.0"
authors = ["Your Name <[email protected]>"]
edition = "2018"

[dependencies]
```

A new key has been added under `[package]`: `edition`. You'll note it's been
set to `2018`. You can also set it to `2015`, which is the default if the key
does not exist.

By using Rust 2018, you unlock some new features that are not allowed in Rust 2015.
And eventually, we'll be turning on some new warnings that help you use these
new features more effectively, as well.

But it's important to note that each package you use can be in either 2015 or
2018 mode, and they work seamlessly together. Your 2018 project can use 2015
dependencies, and a 2015 project can use 2018 dependencies. This ensures that
we don't split the ecosystem, and all of these new things are opt-in,
preserving compatibility for existing code. Furthermore, when you do choose
to migrate Rust 2015 code to Rust 2018, the changes can be made
automatically, via `cargo fix`.

What kind of new features, you may ask? Well, first, features get added to
Rust 2015 unless they require some sort of incompatibility with 2015's
features. As such, most of the language is available everywhere. You can
check out [the edition
guide](https://doc.rust-lang.org/nightly/edition-guide) to check each
feature's minimum `rustc` version as well as edition requirements. However,
there are a few big-ticket features we'd like to mention here: non-lexical
lifetimes, and some module system improvements.

#### Non-lexical lifetimes

If you've been following Rust's development over the past few years, you may
have heard the term "NLL" or "non-lexical lifetimes" thrown around. This is
jargon, but it has a straightforward translation into simpler terms: the
borrow checker has gotten smarter, and now accepts some valid code that it
previously rejected. Consider this example:

```rust
fn main() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use, or at least mention, a more real example? Maybe we should just leave it for @nikomatsakis's post, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A "mutate the original variable at the end of a match" example might be good. IMO That's one of the biggest pain points. Perhaps this example from the RFC?

fn process_or_default() {
    let mut map = ...;
    let key = ...;
    match map.get_mut(&key) { // -------------+ 'lifetime
        Some(value) => process(value),     // |
        None => {                          // |
            map.insert(key, V::default()); // |
            //  ^~~~~~ ERROR.              // |
        }                                  // |
    } // <------------------------------------+
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to stick to the simple example for now, unless we feel really strongly about it. There's a lot in this post already, and keeping it straightforward is more important than being hyper realistic, I think.

If people feel super strongly I'm willing to change it though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@steveklabnik I don't feel super strongly about this. But I'd like to point out that we're likely to get a fair number of people from outside the normal Rust community reading this, who might not have the context for why such a change is important/useful/interesting.

If you're worried about length, perhaps we could link out to a blog post or to the RFC instead of including a more realistic example in the post?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did link to the edition guide, which has the full details of everything; maybe we need another one?

let mut x = 5;

let y = &x;

let z = &mut x;
}
```

In older Rust, this is a compile-time error:

```text
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:18
|
4 | let y = &x;
| - immutable borrow occurs here
5 | let z = &mut x;
| ^ mutable borrow occurs here
6 | }
| - immutable borrow ends here
```

This is because lifetimes follow "lexical scope"; that is, the borrow from `y`
is considered to be held until `y` goes out of scope at the end of main, even
though we never use `y` again. This code is fine, but the borrow checker could
not handle it.

Today, this code will compile just fine.

What if we did use `y`, like this for example:

```rust
fn main() {
let mut x = 5;
let y = &x;
let z = &mut x;

println!("y: {}", y);
}
```

Older Rust will give you this error:

```text
rror[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:18
|
4 | let y = &x;
| - immutable borrow occurs here
5 | let z = &mut x;
| ^ mutable borrow occurs here
...
8 | }
| - immutable borrow ends here
```

With Rust 2018, this error changes for the better:

```text
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:13
|
4 | let y = &x;
| -- immutable borrow occurs here
5 | let z = &mut x;
| ^^^^^^ mutable borrow occurs here
6 |
7 | println!("y: {}", y);
| - borrow later used here
```

Instead of pointing to where `y` goes out of scope, it shows you where the
conflicting borrow occurs. This makes these sorts of errors far easier to
debug.

In Rust 1.31, this feature is exclusive to Rust 2018. We plan to backport it
to Rust 2015 at a later date.

#### Module system changes

The module system can be a struggle for people first learning Rust.
Everyone has their own things that take time to master, of course, but
there's a root cause for why it's so confusing to many: while there are
simple and consistent rules defining the module system, their consequences
can feel inconsistent, counterintuitive and mysterious.

As such, the 2018 edition of Rust introduces a few new module system
features, but they end up simplifying the module system, to make it more
clear as to what is going on.

Here's a brief summary:

* `extern crate` is no longer needed in 99% of circumstances.
* You can import macros with `use`, rather than a `#[macro_use]` attribute.
* Absolute paths begin with a crate name, where the keyword `crate` refers to the current crate.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe clarify: all paths in use statements

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure what you're asking me to tweak.

* A `foo.rs` and `foo/` subdirectory may coexist; `mod.rs` is no longer needed when placing submodules in a subdirectory.

These may seem like arbitrary new rules when put this way, but the mental
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit underwhemling. I'm wondering if we can get across a bit more about why this is an improvement. For example, we could talk about how this eliminates the confusion around paths working differently at the top level module vs submodules.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was worried about the post being too long.... doing that adds a lot of length. I agree that it feels underwhelming though...

model is now significantly simplified overall.

There's a *lot* of details here, so please read [the edition
guide](https://doc.rust-lang.org/nightly/edition-guide/rust-2018/module-system/path-clarity.html)
for full details.

### More lifetime elision rules

Let's talk about a feature that's available in both editions: we've added
some additional elision rules for `impl` blocks and function definitions.
Code like this:

```rust
impl<'a> Reader for BufReader<'a> {
// methods go here
}
`

can now be written like this:

```rust
impl Reader for BufReader<'_> {
// methods go here
}
```

The `'_` lifetime still shows that `BufReader` takes a parameter, but we
don't need to create a name for it anymore.

Lifetimes are still required to be defined in structs. We're considering
some options here in the future, but have no concrete plans yet.

### `const fn`

There's several ways to define a function in Rust: a regular function with
`fn`, an unsafe function with `unsafe fn`, an external function with `extern fn`.
This release adds a new way to qualify a function: `const fn`. It looks like
this:

```rust
const fn foo(x: i32) -> i32 {
x + 1
}
```

A `const fn` can be called like a regular function, but it can also be used
in any constant context. When it is, it is evaluated at compile time, rather
than at run time. As an example:

```rust
const SIX: i32 = foo(5);
```

This will execute `foo` at compile time, and set `SIX` to `6`.

`const fn`s cannot do everything that normal `fn`s can do; they must
have deterministic output. This is important for soundness reasons.
Currently, `const fn`s can do a minimal subset of operations. Here's
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks accurate / what we said in the tracking issue / reference;
Are you looking for something specific?
cc @oli-obk

some examples of what you can do:

* Arithmetic and comparison operators on integers
* All boolean operators except for `&&` and `||`
* Constructing arrays, structs, enums, and tuples
* Calls to other `const fn`s
* Index expressions on arrays and slices
* Field accesses on structs and tuples
* Reading from constants (but not statics, not even taking a reference to a static)
* `&` and `*` of references
* Casts, except for raw pointer to integer casts

We'll be slowly growing the abilities of `const fn`, but we've decided that
this is enough useful stuff to start shipping the feature itself.

For full details, please see [the
reference](https://doc.rust-lang.org/reference/items/functions.html#const-functions).

### Tool lints

In [Rust 1.30](https://blog.rust-lang.org/2018/10/25/Rust-1.30.0.html), we
stabilized "tool attributes", like `#[rustfmt::skip]`. In Rust 1.31, we're
stabilizing something similar: "tool lints," like
`#[allow(clippy::something)]` These give a namespace to lints, so that it's
more clear which tool they're coming from.

If you previously used Clippy's lints, you can migrate like this:

```rust
// old
#![allow(bool_comparison)]

// new
#![allow(clippy::bool_comparison)]
```

You'll get warnings that can help you update to the new style.

### Library stabilizations

A bunch of `From` implementations have been added:

* `u8` now implements `From<NonZeroU8>`, and likewise for the other numeric types and their `NonZero` equivalents
* `Option<&T>` implements `From<&Option<T>>`, and likewise for `&mut`

Additionally, these functions have been stabilized:

* [`slice::align_to`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to) and its mutable counterpart
* [`slice::chunks_exact`](https://doc.rust-lang.org/std/primitive.slice.html#method.chunks_exact),
as well as its mutable and `r` counterparts (like
[`slice::rchunks_exact_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.rchunks_mut)) in all combinations

See the [detailed release notes][notes] for more.

### Cargo features

Cargo will now download packages in parallel using HTTP/2. Additionally,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should state how much quicker this is, will follow up...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Up to 20x faster"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have these numbers? how reliable are they?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have the detail. But I think they are reliable (the number is from a GH issue, so it must be true).

now that `extern crate` is gone, you can't `extern crate foo as bar;` anymore
to rename a crate. As such, you can do so in your `Cargo.toml`, like this:

```toml
[dependencies]
baz = { version = "0.1", package = "foo" }
```

or, the equivalent

```toml
[dependencies.baz]
version = "0.1"
package = "foo"
```

Now, the `baz` package will be able to be used via `foo` in your code.

See the [detailed release notes][notes] for more.

## Contributors to 1.31.0

Many people came together to create Rust 1.31. We couldn't have done it
without all of you. [Thanks!](https://thanks.rust-lang.org/rust/1.31.0)