Series: The Rust Annals

Vol. I Issue 13 nlopes.dev

Announcing Rust 1.12

The new diagnostic format, default MIR backend, and Cargo workspaces make this one of the most significant Rust releases since 1.0.

Rust 1.12 makes the compiler’s overhauled, source-centered diagnostics broadly available and enables MIR-based code generation by default. The new intermediate representation already improves compile times and code size for some codebases while preparing the compiler for further analysis and optimization work. Cargo workspaces, dependency-source overrides, new ARM MUSL targets, and a broad set of standard-library improvements make this a substantial release for both application and tooling authors.

Diagnostics put the relevant source code front and center

The largest user-facing change is rustc’s new error format. Instead of describing a failure largely apart from its source, diagnostics highlight the relevant parts of the code and annotate what went wrong. For a trait implementation that does not match its declaration, for example, the new presentation points to the relevant line in each and labels why they differ. Although initially designed to make borrow-checking errors easier to understand, the format proved applicable to many kinds of errors. Error descriptions involving references and unknown numeric types are also more human-friendly.

Tooling can request structured versions of these diagnostics with --error-format=json. The announcement’s borrow-checking example produces output of this form; part of the full output was elided in the source:

$ rustc borrowck-assign-comp.rs --error-format=json
{"message":"cannot assign to `p.x` because it is borrowed","level":"error","spans":[{"file_name":"borrowck-assign-comp.rs","byte_start":562,"byte_end":563,"line_start":15,"line_end":15,"column_start":14,"column_end":15,"is_primary":false,"text":[{"text":"    let q = &p;","highlight_start":14,"highlight_end":15}],"label":"borrow of `p.x` occurs here","suggested_replacement":null,"expansion":null}],"label":"assignment to borrowed `p.x` occurs here","suggested_replacement":null,"expansion":null}],"children":[],"rendered":null}
{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":null}

This JSON output is primarily intended for IDEs and other development tools.

MIR becomes the default code-generation path

The largest internal change is the default use of the new mid-level intermediate representation, MIR, for the pass that generates LLVM IR, which rustc calls “translation.” MIR gives the compiler a simpler representation of Rust code than operating entirely on the abstract syntax tree, making historically difficult analyses and optimizations easier to implement correctly and compiler passes easier to verify.

MIR exposes complete information about control flow, including whether values have moved and whether their destructors must run. When a value may or may not have moved by the end of a scope, the compiler can use one stack bitflag that is easier for LLVM’s optimization passes to reason about. This means less compiler work and less runtime bloat. Although the new backend has no universal user-visible effect yet, some codebases already see shorter compile times and smaller code.

The compiler can also now be built against LLVM 3.9. Test binaries accept --test-threads to select the number of test-running threads, with the same behavior as the RUST_TEST_THREADS environment variable, and the test runner warns when tests exceed 60 seconds.

Targets, sources, and standard-library conveniences broaden practical coverage

rustc adds the arm-unknown-linux-musleabi, arm-unknown-linux-musleabihf, and armv7-unknown-linux-musleabihf targets. They produce statically linked binaries, but binary release builds were not yet available. Rust releases now include source packages installable with rustup component add rust-src; tools and IDEs can use the installed source under the sysroot’s lib/rustlib/src. Documentation also received many minor improvements.

The standard library adds or stabilizes the following APIs and implementations:

Linux sockets are now correctly closed in subprocesses through SOCK_CLOEXEC, and Unicode definitions are updated to Unicode 9.0.

Cargo workspaces coordinate multi-package projects

Cargo’s biggest feature in this cycle is workspaces, defined by RFC 1525. A workspace lets a group of Rust packages share one Cargo.lock, making it easier for a project split across packages to keep shared dependencies on one version. Most multi-package projects can enable it by adding [workspace] to the top-level Cargo.toml; more complex arrangements may require additional configuration.

Cargo can also override a crate’s source. Together with cargo-vendor and cargo-local-registry, this permits robust local dependency vendoring. The support was also expected eventually to underpin mirrors of crates.io.

Other Cargo changes speed up no-op registry updates, add --lib to cargo new, report the compilation profile after compiling, and add --dry-run to cargo publish.

Reproduced from the Rust blog under its publication licence. Typeset in Literata.