The Rust team is happy to announce the latest version of Rust, 1.16.0. Rust is a systems programming language focused on safety, speed, and concurrency.
If you have a previous version of Rust installed, getting Rust 1.16 is as easy as:
$ rustup update stable
If you don't have it already, you can get rustup
from the
appropriate page on our website, and check out the detailed release notes for
1.16.0 on GitHub.
What's in 1.16.0 stable
The largest addition to Rust 1.16 is cargo check
. This new subcommand should speed up the development
workflow in many cases.
What does it do? Let's take a step back and talk about how rustc
compiles your code. Compilation has many
"passes", that is, there are many distinct steps that the compiler takes on the road from your source code
to producing the final binary. You can see each of these steps (and how much time and memory they take)
by passing -Z time-passes
to a nightly compiler:
rustc +nightly hello.rs -Z time-passes
time: 0.003; rss: 16MB parsing
time: 0.000; rss: 16MB recursion limit
time: 0.000; rss: 16MB crate injection
time: 0.000; rss: 16MB plugin loading
time: 0.000; rss: 16MB plugin registration
time: 0.049; rss: 34MB expansion
<snip>
There's a lot of them. However, you can think of this process in two big steps: first, rustc
does
all of its safety checks, makes sure your syntax is correct, all that stuff. Second, once it's satisfied
that everything is in order, it produces the actual binary code that you end up executing.
It turns out that that second step takes a lot of time. And most of the time, it's not neccesary. That is, when you're working on some Rust code, many developers will get into a workflow like this:
- Write some code.
- Run
cargo build
to make sure it compiles. - Repeat 1-2 as needed.
- Run
cargo test
to make sure your tests pass. - GOTO 1.
In step two, you never actually run your code. You're looking for feedback from the compiler, not to
actually run the binary. cargo check
supports exactly this use-case: it runs all of the compiler's
checks, but doesn't produce the final binary.
So how much speedup do you actually get? Like most performance related questions, the answer is "it depends." Here are some very un-scientific benchmarks:
thanks | cargo | diesel | |
---|---|---|---|
initial build | 134.75s | 236.78s | 15.27s |
initial check | 50.88s | 148.52s | 12.81s |
speedup | 2.648 | 1.594 | 1.192 |
secondary build | 15.97s | 64.34s | 13.54s |
secondary check | 2.9s | 9.29s | 12.3s |
speedup | 5.506 | 6.925 | 1.100 |
The 'initial' categories are the first build after cloning down a project. The 'secondary' categories
involved adding one blank line to the top of src\lib.rs
and running the command again. That's why
the initial ones are more dramatic; they involve also doing this for all dependencies, as well as
the crate itself. As you can see, larger projects with many dependencies see a big improvement, but
smaller ones see much more modest gains.
We are still working on improving compile-times generally as well, though we don't have anything in particular to highlight at this time.
Other improvements
To support cargo check
, rustc
has learned to emit a new kind of file: .rmeta
. This file
will contain only the metadata about a particular crate. cargo check
needs this for your
dependencies, to let the compiler check types and such from them. It's also useful for the
Rust Language Server, and possibly more tools in the future.
Another large change is the removal of a long-standing diagnostic: consider using an explicit lifetime parameter
. This diagnostic would kick in whenever you had an incorrect lifetime annotation,
and the compiler thought that you might have meant something else. Consider this code:
use std::str::FromStr;
pub struct Name<'a> {
name: &'a str,
}
impl<'a> FromStr for Name<'a> {
type Err = ();
fn from_str(s: &str) -> Result<Name, ()> {
Ok(Name { name: s })
}
}
Here, Rust isn't sure what to do with the lifetimes; as written, the code doesn't guarantee that s
will live as long as Name
, which is required for Name
to be valid. Let's try to compile this
code with Rust 1.15.1:
$ rustc +1.15.1 foo.rs --crate-type=lib
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements
--> .\foo.rs:10:5
|
10 | fn from_str(s: &str) -> Result<Name, ()> {
| _____^ starting here...
11 | | Ok(Name { name: s })
12 | | }
| |_____^ ...ending here
|
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name, ()>
--> .\foo.rs:10:5
|
10 | fn from_str(s: &str) -> Result<Name, ()> {
| _____^ starting here...
11 | | Ok(Name { name: s })
12 | | }
| |_____^ ...ending here
The compiler explains the issue, and gives a helpful suggestion. So let's try it: modify the code to add in
the 'a
, and compile again:
$ rustc +1.15.1 .\foo.rs --crate-type=lib
error[E0308]: method not compatible with trait
--> .\foo.rs:10:5
|
10 | fn from_str(s: &'a str) -> Result<Name, ()> {
| _____^ starting here...
11 | | Ok(Name { name: s })
12 | | }
| |_____^ ...ending here: lifetime mismatch
|
<snip>
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
--> .\foo.rs:10:5
|
10 | fn from_str(s: &'a str) -> Result<Name, ()> {
| _____^ starting here...
11 | | Ok(Name { name: s })
12 | | }
| |_____^ ...ending here
It still doesn't work. That help message was not actually helpful. It does suggest adding another
lifetime, this time on Name
. If we do that...
$ rustc +1.15.1 .\foo.rs --crate-type=lib
<snip>
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
--> .\foo.rs:10:5
... that's what we already have, compiler!
This diagnostic was well-intentioned, but when it's wrong, it was very wrong, as you can see here. Sometimes it wouldn't even suggest valid Rust syntax! Furthermore, more advanced Rust users didn't really need the suggestion, but new Rustaceans would take them to heart, and then be led down this bad path. As such, we decided that for now, we should remove the help message entirely. We may bring it back in the future, but only if we can limit false positives.
An aside: the above implementation is not possible;
Name
would need to useString
, not&str
.
In other diagnostic changes, previous versions of Rust would helpfully attempt to suggest fixes for typos:
let foo = 5;
println!("{}", ffo);
Would give this error:
error[E0425]: cannot find value `ffo` in this scope
--> foo.rs:4:20
|
4 | println!("{}", ffo);
| ^^^ did you mean `foo`?
However, this would only happen in certain circumstances: sometimes in local variables, and for fields in structs. This now happens nearly everywhere. When combined with some other related improvements, this results in a significant improvement in these sorts of diagnostics.
See the detailed release notes for more.
Library stabilizations
21 new bits of API were stabilized this release:
VecDeque::truncate
VecDeque::resize
String::insert_str
Duration::checked_add
Duration::checked_sub
Duration::checked_div
Duration::checked_mul
str::replacen
str::repeat
SocketAddr::is_ipv4
SocketAddr::is_ipv6
IpAddr::is_ipv4
IpAddr::is_ipv6
Vec::dedup_by
Vec::dedup_by_key
Result::unwrap_or_default
<*const T>::wrapping_offset
<*mut T>::wrapping_offset
CommandExt::creation_flags
File::set_permissions
String::split_off
In addition, a number of small improvements to existing functions landed. For example writeln!
now has
a single-argument form, just like println!
has. This ends up writing only a newline, but is a nice bit
of symmetry.
All structs in the standard library now implement Debug
.
When slicing a &str
, you'll see better errors. For example, this code:
&"abcαβγ"[..4]
Is incorrect. It generates this error:
thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not
a char boundary; it is inside 'α' (bytes 3..5) of `abcαβγ`'
The part after the ;
is new.
See the detailed release notes for more.
Cargo features
In addition to cargo check
, Cargo and crates.io have some new polish added. For example,
cargo build
and cargo doc
now take a --all
flag for building and documenting every
crate in your workspace with one command.
Cargo now has a --version --verbose
flag, mirroring rustc
.
Crates.io now can show off your TravisCI or AppVeyor badges on your crate's page.
In addition, both Cargo and crates.io understand categories. Unlike keywords, which are free-form, categories are curated. In addition, keywords are used for searching, but categories are not. In other words, categories are intended to assist browsing, and keywords are intended to assist searching.
You can browse crates by category here.
See the detailed release notes for more.
Contributors to 1.16.0
Last release, we introduced thanks.rust-lang.org. We have been doing some behind-the-scenes refactoring work to allow for more projects than only Rust itself; we're hoping to introduce that in the next release.
We had 135 individuals contribute to Rust 1.16. Thanks!