Series: The Rust Annals

Vol. I Issue 18 nlopes.dev

Announcing Rust 1.17

Rust 1.17 is chiefly a quality-of-life release: constants and statics need fewer lifetime annotations, struct construction gains field init shorthand, and Cargo can avoid building targets whose required features are disabled. It also completes Rust’s move to its Cargo-driven build system, expands raw-pointer, collection, cell, networking, and conversion APIs, and makes common string-concatenation errors and default backtraces easier to understand.

Constants, statics, and struct construction shed repetition

The 'static lifetime is now assumed in constants and statics, where it is the only lifetime that makes sense. Explicit annotations remain valid, but code can be shortened as follows:

// old
const NAMES: &'static [&'static str; 2] = &["Ferris", "Bors"];

// new
const NAMES: &[&str; 2] = &["Ferris", "Bors"];

Field init shorthand removes another common duplication. When a field name and an in-scope variable have the same name, the field name alone supplies that variable as its value:

// definitions
struct Point {
    x: i32,
    y: i32,
}

let x = 5;
let y = 6;

// old
let p = Point {
    x: x,
    y: y,
};

// new
let p = Point {
    x,
    y,
};

Cargo recognizes conventional build scripts and feature-gated targets

Cargo now assumes a root-level build.rs is the package’s build script, so the usual build = "build.rs" declaration is unnecessary. Cargo had warned about this change for the preceding few releases; packages that contain the file but do not want it used as a build script can set build = false.

The new required-features manifest key specifies features that must be enabled before Cargo builds a target. Previously, a feature-specific binary could need alternate main functions merely to accommodate cargo build attempting every target:

#[cfg(not(all(feature = "postgres", feature = "tools")))]
fn main() {
    println!("This tool requires the `postgres` and `tools` features to be enabled.");
}

#[cfg(all(feature = "postgres", feature = "tools"))]
fn main() {
    // real code
}

A target can now express the constraint in Cargo.toml instead:

[[bin]]
# ...
required-features = ["postgres", "tools"]

With that declaration, cargo build builds the target only when both features are set, allowing the binary to use a normal fn main. This is also useful for examples whose feature gating is relevant inside the package rather than to readers reusing the example.

Cargo also adds support for cargo check --all and cargo run --package. Users may opt in to ignoring SSL revocation checks; checking remains the default.

Rust’s Cargo-driven build system becomes the only build system

The old Makefile-based Rust build system has been removed. Its Rust-written replacement, announced in Rust 1.15 and primarily driven by Cargo, is now mature enough to be the sole build system. This also permits crates.io packages inside Rust’s own build process: mdBook was the first addition and now renders The Book, The Reference, and The Nomicon.

Those three books have moved to their own repositories: The Book repository, The Reference repository, and The Nomicon repository. A fourth, still in-tree publication, The Unstable Book, gives an overview of unstable features by name, links to their tracking issues, and may contain initial documentation.

Documentation is installed by default again. Rustup had stopped doing so a few releases earlier to save bandwidth and because not everyone wanted a local copy, but some users discovered the absence only after losing internet access while others wanted local documentation regardless of connectivity.

On Windows, Rust 1.17 cannot automatically detect Visual Studio 2017 because Microsoft changed its installation structure. Work on the necessary detection changes did not reach this release.

The standard library adds pointer, range, cell, conversion, and process APIs

Nineteen APIs stabilize in this release:

  • Arc::into_raw and Rc::into_raw consume an Arc or Rc and return a raw pointer.
  • Arc::from_raw and Rc::from_raw turn that raw pointer back into an Arc or Rc.
  • Arc::ptr_eq and Rc::ptr_eq report whether two values point to the same allocation, rather than merely comparing equal.
  • Ordering::then chains two Ordering values; Ordering::then_with does so with a function.
  • BTreeMap::range iterates over part of a BTreeMap, while BTreeMap::range_mut does so mutably; collections::Bound provides additional control.
  • process::abort completely terminates a process abnormally.
  • ptr::read_unaligned and ptr::write_unaligned correspond to ptr::read and ptr::write without alignment requirements.
  • Result::expect_err mirrors Result::expect for the Err case instead of the Ok case.
  • Cell::swap resembles std::mem::swap but operates through &Cell rather than &mut T.
  • Cell::replace resembles std::mem::replace with the same &Cell distinction.
  • Cell::into_inner consumes a Cell and extracts its value.
  • Cell::take removes a value from a Cell, leaving Default::default behind.

Many Cell<T> methods have also substantially relaxed their former T: Copy requirement. Box<T> implements more than a dozen additional From conversions, and SocketAddr and IpAddr gain new conversions.

The address conversions can replace runtime string parsing:

// old
"127.0.0.1:3000".parse().unwrap()

// new
SocketAddr::from(([127, 0, 0, 1], 3000))
// or even
([127, 0, 0, 1], 3000).into()

This removes unnecessary runtime parsing and is roughly as readable, depending on preference.

Diagnostics explain string ownership and backtraces become easier to scan

Adding two &str values remains invalid—concatenation supports String + &str—but the compiler now explains the ownership requirement and proposes converting the left operand:

// code
"foo" + "bar"

// old
error[E0369]: binary operation `+` cannot be applied to type `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^
  |
note: an implementation of `std::ops::Add` might be missing for `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^

// new
error[E0369]: binary operation `+` cannot be applied to type `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^
  |
  = note: `+` can't be used to concatenate two `&str` strings
help: to_owned() can be used to create an owned `String` from a string
reference. String concatenation appends the string on the right to the string on
the left and may require reallocation. This requires ownership of the string on
the left.
  |     "foo".to_owned() + "bar"

Default backtraces now elide addresses and symbol hashes, producing a more compact trace. For example, the former full output:

thread 'main' panicked at 'explicit panic', foo.rs:2
stack backtrace:
   1:     0x55c39a23372c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
   2:     0x55c39a23571e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:351
   3:     0x55c39a235324 - std::panicking::default_hook::h1670459d2f3f8843
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:367
   4:     0x55c39a235afb - std::panicking::rust_panic_with_hook::hcf0ddb069e7beee7
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:555
   5:     0x55c39a22e866 - std::panicking::begin_panic::heb433e9aa28a7408
   6:     0x55c39a22e9bf - foo::main::hd216d4a160fcce19
   7:     0x55c39a23d44a - __rust_maybe_catch_panic
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libpanic_unwind/lib.rs:98
   8:     0x55c39a236006 - std::rt::lang_start::hd7c880a37a646e81
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:436
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panic.rs:361
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/rt.rs:57
   9:     0x55c39a22e9e9 - main
  10:     0x7f5e5ed3382f - __libc_start_main
  11:     0x55c39a22e6b8 - _start
  12:                0x0 - <unknown>

is displayed by default as:

thread 'main' panicked at 'explicit panic', foo.rs:2
stack backtrace:
   0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
             at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::_print
             at /checkout/src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at /checkout/src/libstd/sys_common/backtrace.rs:60
             at /checkout/src/libstd/panicking.rs:355
   3: std::panicking::default_hook
             at /checkout/src/libstd/panicking.rs:371
   4: std::panicking::rust_panic_with_hook
             at /checkout/src/libstd/panicking.rs:549
   5: std::panicking::begin_panic
   6: foo::main
   7: __rust_maybe_catch_panic
             at /checkout/src/libpanic_unwind/lib.rs:98
   8: std::rt::lang_start
             at /checkout/src/libstd/panicking.rs:433
             at /checkout/src/libstd/panic.rs:361
             at /checkout/src/libstd/rt.rs:57
   9: main
  10: __libc_start_main
  11: _start

Set RUST_BACKTRACE=full to request the full backtrace. Further cleanup may be possible in the future.

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