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

As always, you can install Rust 1.13.0 from the appropriate page on our website, and check out the detailed release notes for 1.13.0 on GitHub. 1448 patches were landed in this release.

It's been a busy season in Rust. We enjoyed three Rust conferences, RustConf, RustFest, and Rust Belt Rust, in short succession. It was great to see so many Rustaceans in person, some for the first time! We've been thinking a lot about the future, developing a roadmap for 2017, and building the tools our users tell us they need.

And even with all that going on, we put together a new release filled with fun new toys.

What's in 1.13 stable

The 1.13 release includes several extensions to the language, including the long-awaited ? operator, improvements to compile times, minor feature additions to cargo and the standard library. This release also includes many small enhancements to documentation and error reporting, by many contributors, that are not individually mentioned in the release notes.

This release contains important security updates to Cargo, which depends on curl and OpenSSL, which both published security updates recently. For more information see the respective announcements for curl 7.51.0 and OpenSSL 1.0.2j.

The ? operator

Rust has gained a new operator, ? , that makes error handling more pleasant by reducing the visual noise involved. It does this by solving one simple problem. To illustrate, imagine we had some code to read some data from a file:

fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("username.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } }

This code has two paths that can fail, opening the file and reading the data from it. If either of these fail to work, we'd like to return an error from read_username_from_file . Doing so involves match ing on the result of the I/O operations. In simple cases like this though, where we are only propagating errors up the call stack, the matching is just boilerplate - seeing it written out, in the same pattern every time, doesn't provide the reader with a great deal of useful information.

With ? , the above code looks like this:

fn read_username_from_file() -> Result<String, io::Error> { let mut f = File::open("username.txt")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) }

The ? is shorthand for the entire match statements we wrote earlier. In other words, ? applies to a Result value, and if it was an Ok , it unwraps it and gives the inner value. If it was an Err , it returns from the function you're currently in. Visually, it is much more straightforward. Instead of an entire match statement, now we are just using the single "?" character to indicate that here we are handling errors in the standard way, by passing them up the call stack.

Seasoned Rustaceans may recognize that this is the same as the try! macro that's been available since Rust 1.0 . And indeed, they are the same. Before 1.13, read_username_from_file could have been implemented like this:

fn read_username_from_file() -> Result<String, io::Error> { let mut f = try!(File::open("username.txt")); let mut s = String::new(); try!(f.read_to_string(&mut s)); Ok(s) }

So why extend the language when we already have a macro? There are multiple reasons. First, try! has proved to be extremely useful, and is used often in idiomatic Rust. It is used so often that we think it's worth having a sweet syntax. This sort of evolution is one of the great advantages of a powerful macro system: speculative extensions to the language syntax can be prototyped and iterated on without modifying the language itself, and in return, macros that turn out to be especially useful can indicate missing language features. This evolution, from try! to ? is a great example.

One of the reasons try! needs a sweeter syntax is that it is quite unattractive when multiple invocations of try! are used in succession. Consider:

try!(try!(try!(foo()).bar()).baz())

as opposed to

foo()?.bar()?.baz()?

The first is quite difficult to scan visually, and each layer of error handling prefixes the expression with an additional call to try! . This brings undue attention to the trivial error propagation, obscuring the main code path, in this example the calls to foo , bar and baz . This sort of method chaining with error handling occurs in situations like the builder pattern.

Finally, the dedicated syntax will make it easier in the future to produce nicer error messages tailored specifically to ? , whereas it is difficult to produce nice errors for macro-expanded code generally (in this release, though, the ? error messages could use improvement).

Though this is a small feature, in our experience so far, ? feels like a solid ergonomic improvement to the old try! macro. This is a good example of the kinds of incremental, quality-of-life improvements Rust will continue to receive, polishing off the rough corners of our already-powerful base language.

Read more about ? in RFC 243.

Performance improvements

There has been a lot of focus on compiler performance lately. There's good news in this release, and more to come.

Mark Simulacrum and Nick Cameron have been refining perf.rust-lang.org, our tool for tracking compiler performance. It runs the rustc-benchmarks suite regularly, on dedicated hardware, and tracks the results over time. This tool records the results for each pass in the compiler and is used by the compiler developers to narrow commit ranges of performance regressions. It's an important part of our toolbox!

We can use this tool to look at a graph of performance over the 1.13 development cycle, shown below. This cycle covered the dates from August 16 through September 29 (the graph begins from Augest 25th though and is filtered in a few ways to eliminate bogus, incomplete, or confusing results). There appear to be some big reductions, which are quantified on the corresponding statistics page.

The big improvement demonstrated in the graphs, on September 1, is from an optimization from Niko to cache normalized projections during translation. That is to say, during generation of LLVM IR, the compiler no longer recomputes concrete instances of associated types each time they are needed, but instead reuses previously-computed values. This optimization doesn't affect all code bases, but in code bases that exhibit certain patterns, like futures-rs, where debug mode build-time improved by up to 40%, you'll notice the difference.

Another such optimization, that doesn't affect every crate but does affect some in a big way, came from Michael Woerister, and improves compile time for crates that export many inline functions. When a function is marked #[inline] , in addition to translating that function for use by the current crate, the compiler stores its MIR representation in the crate rlib, and translates the function to LLVM IR in every crate that calls it. The optimization Michael did is obvious in retrospect: there are some cases where inline functions are only for the consumption of other crates, and never called from the crate in which they are defined; so the compiler doesn't need to translate code for inline functions in the crate they are defined unless they are called directly. This saves the cost of rustc converting the function to LLVM IR and LLVM optimizing and converting the function to machine code.

In some cases this results in dramatic improvements. Build times for the ndarray crate improved by 50%, and in the (unreleased) winapi 0.3 crate, rustc now emits no machine code at all.

But wait, there's more still! Nick Nethercote has turned his focus to compiler performance as well, focusing on profiling and micro-optimizations. This release contains several fruits of his work, and there are more in the pipeline for 1.14.

Other notable changes

This release contains important security updates to Cargo, which depends on curl and OpenSSL, which both published security updates recently. For more information see the respective announcements for curl 7.51.0 and OpenSSL 1.0.2j.

Macros can now be used in type position (RFC 873), and attributes can be applied to statements (RFC 16):

// Use a macro to name a type macro_rules! Tuple { { $A:ty,$B:ty } => { ($A, $B) } } let x: Tuple!(i32, i32) = (1, 2);

// Apply a lint attribute to a single statement #[allow(non_snake_case)] let BAD_STYLE = List::new();

Inline drop flags have been removed. Previously, in case of a conditional move, the compiler would store a "drop flag" inline in a struct (increasing its size) to keep track of whether or not it needs to be dropped. This means that some structs take up some unexpected extra space, which interfered with things like passing types with destructors over FFI. It also was a waste of space for code that didn't have conditional moves. In 1.12, MIR became the default, which laid the groundwork for many improvements, including getting rid of these inline drop flags. Now, drop flags are stored in an extra slot on the stack frames of functions that need them.

1.13 contains a serious bug in code generation for ARM targets using hardware floats (which is most ARM targets). ARM targets in Rust are presently in our 2nd support tier, so this bug was not determined to block the release. Because 1.13 contains a security update, users that must target ARM are encouraged to use the 1.14 betas, which will soon get a fix for ARM.

Language stabilizations

Library stabilizations

Cargo features

See the detailed release notes for more.

Contributors to 1.13.0

We had 155 individuals contribute to 1.13.0. Thank you so much!