|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Announcing Rust 1.16" |
| 4 | +author: The Rust Core Team |
| 5 | +--- |
| 6 | + |
| 7 | +The Rust team is happy to announce the latest version of Rust, 1.16.0. Rust is a |
| 8 | +systems programming language focused on safety, speed, and concurrency. |
| 9 | + |
| 10 | +If you have a previous version of Rust installed, getting Rust 1.16 is as easy as: |
| 11 | + |
| 12 | +```bash |
| 13 | +$ rustup update stable |
| 14 | +``` |
| 15 | + |
| 16 | +If you don't have it already, you can [get `rustup`][install] from the |
| 17 | +appropriate page on our website, and check out the [detailed release notes for |
| 18 | +1.16.0][notes] on GitHub. |
| 19 | + |
| 20 | +[install]: https://www.rust-lang.org/install.html |
| 21 | +[notes]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1160-2017-03-16 |
| 22 | + |
| 23 | +### What's in 1.16.0 stable |
| 24 | + |
| 25 | +The largest addition to Rust 1.16 is `cargo check`. This new subcommand should speed up the development |
| 26 | +workflow in many cases. |
| 27 | + |
| 28 | +What does it do? Let's take a step back and talk about how `rustc` compiles your code. Compilation has many |
| 29 | +"passes", that is, there are many distinct steps that the compiler takes on the road from your source code |
| 30 | +to producing the final binary. You can see each of these steps (and how much time and memory they take) |
| 31 | +by passing `-Z time-passes` to a nightly compiler: |
| 32 | + |
| 33 | + ```text |
| 34 | + rustc .\hello.rs -Z time-passes |
| 35 | +time: 0.003; rss: 16MB parsing |
| 36 | +time: 0.000; rss: 16MB recursion limit |
| 37 | +time: 0.000; rss: 16MB crate injection |
| 38 | +time: 0.000; rss: 16MB plugin loading |
| 39 | +time: 0.000; rss: 16MB plugin registration |
| 40 | +time: 0.049; rss: 34MB expansion |
| 41 | +<snip> |
| 42 | + ``` |
| 43 | + |
| 44 | +There's a lot of them. However, you can think of this process in two big steps: first, `rustc` does |
| 45 | +all of its safety checks, makes sure your syntax is correct, all that stuff. Second, once it's satisfied |
| 46 | +that everything is in order, it produces the actual binary code that you end up executing. |
| 47 | + |
| 48 | +It turns out that that second step takes a lot of time. And most of the time, it's not neccesary. That is, |
| 49 | +when you're working on some Rust code, many developers will get into a workflow like this: |
| 50 | + |
| 51 | + 1. Write some code. |
| 52 | + 2. Run `cargo build` to make sure it compiles. |
| 53 | + 3. Repeat 1-2 as needed. |
| 54 | + 4. Run `cargo test` to make sure your tests pass. |
| 55 | + 5. GOTO 1. |
| 56 | + |
| 57 | +In step two, you never actually run your code. You're looking for feedback from the compiler, not to |
| 58 | +actually run the binary. `cargo check` supports exactly this use-case: it runs all of the compiler's |
| 59 | +checks, but doesn't produce the final binary. |
| 60 | + |
| 61 | +So how much speedup do you actually get? Like most performance related questions, the answer is "it |
| 62 | +depends." Here are some very un-scientific benchmarks: |
| 63 | + |
| 64 | +| | initial build | initial check | speedup | secondary build | secondary check | speedup | |
| 65 | +|--------|---------------|---------------|---------|-----------------|-----------------|---------| |
| 66 | +| thanks | 134.75s | 50.88s | 2.648 | 15.97s | 2.9s | 5.506 | |
| 67 | +| cargo | 236.78s | 148.52s | 1.594 | 64.34s | 9.29s | 6.925 | |
| 68 | +| diesel | 15.27s | 12.81s | 0.015 | 13.54s | 12.3s | 1.100 | |
| 69 | + |
| 70 | +The 'initial' categories are the first build after cloning down a project. The 'secondary' categories |
| 71 | +involved adding one blank line to the top of `src\lib.rs` and running the command again. That's why |
| 72 | +the initial ones are more dramatic; they involve also doing this for all dependencies, as well as |
| 73 | +the crate itself. As you can see, larger projects with many dependencies see a big improvement, but |
| 74 | +smaller ones see much more modest gains. |
| 75 | + |
| 76 | +We are still working on improving compile-times generally as well, though we don't have anything |
| 77 | +in particular to highlight at this time. |
| 78 | + |
| 79 | +#### Other improvements |
| 80 | + |
| 81 | +To support `cargo check`, `rustc` has [learned to emit] a new kind of file: `.rmeta`. This file |
| 82 | +will contain only the metadata about a particular crate. `cargo check` needs this for your |
| 83 | +dependencies, to let the compiler check types and such from them. It's also useful for the |
| 84 | +[Rust Language Server], and possibly more tools in the future. |
| 85 | + |
| 86 | +[learned to emit]: https://github.com/rust-lang/rust/pull/38571 |
| 87 | +[Rust Language Server]: https://github.com/rust-lang-nursery/rls |
| 88 | + |
| 89 | +Another large change is the removal of a long-standing diagnostic: `consider using an explicit |
| 90 | +lifetime parameter`. This diagnostic would kick in whenever you had an incorrect lifetime annotation, |
| 91 | +and the compiler thought that you might have meant something else. Consider this code: |
| 92 | + |
| 93 | +```rust |
| 94 | +use std::str::FromStr; |
| 95 | + |
| 96 | +pub struct Name<'a> { |
| 97 | + name: &'a str, |
| 98 | +} |
| 99 | + |
| 100 | +impl<'a> FromStr for Name<'a> { |
| 101 | + type Err = (); |
| 102 | + |
| 103 | + fn from_str(s: &str) -> Result<Name, ()> { |
| 104 | + Ok(Name { name: s }) |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +Here, Rust isn't sure what to do with the lifetimes; as written, the code doesn't guarantee that `s` |
| 110 | +will live as long as `Name`, which is required for `Name` to be valid. Let's try to compile this |
| 111 | +code with Rust 1.15.1: |
| 112 | + |
| 113 | +```bash |
| 114 | +> rustc +1.15.1 foo.rs --crate-type=lib |
| 115 | +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements |
| 116 | + --> .\foo.rs:10:5 |
| 117 | + | |
| 118 | +10 | fn from_str(s: &str) -> Result<Name, ()> { |
| 119 | + | _____^ starting here... |
| 120 | +11 | | Ok(Name { name: s }) |
| 121 | +12 | | } |
| 122 | + | |_____^ ...ending here |
| 123 | + | |
| 124 | +help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name, ()> |
| 125 | + --> .\foo.rs:10:5 |
| 126 | + | |
| 127 | +10 | fn from_str(s: &str) -> Result<Name, ()> { |
| 128 | + | _____^ starting here... |
| 129 | +11 | | Ok(Name { name: s }) |
| 130 | +12 | | } |
| 131 | + | |_____^ ...ending here |
| 132 | +``` |
| 133 | +
|
| 134 | +The compiler explains the issue, and gives a helpful suggestion. So let's try it: modify the code to add in |
| 135 | +the `'a`, and compile again: |
| 136 | +
|
| 137 | +```bash |
| 138 | +> rustc +1.15.1 .\foo.rs --crate-type=lib |
| 139 | +error[E0308]: method not compatible with trait |
| 140 | + --> .\foo.rs:10:5 |
| 141 | + | |
| 142 | +10 | fn from_str(s: &'a str) -> Result<Name, ()> { |
| 143 | + | _____^ starting here... |
| 144 | +11 | | Ok(Name { name: s }) |
| 145 | +12 | | } |
| 146 | + | |_____^ ...ending here: lifetime mismatch |
| 147 | + | |
| 148 | +<snip> |
| 149 | +help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()> |
| 150 | + --> .\foo.rs:10:5 |
| 151 | + | |
| 152 | +10 | fn from_str(s: &'a str) -> Result<Name, ()> { |
| 153 | + | _____^ starting here... |
| 154 | +11 | | Ok(Name { name: s }) |
| 155 | +12 | | } |
| 156 | + | |_____^ ...ending here |
| 157 | +``` |
| 158 | +
|
| 159 | +It still doesn't work. That help message was not actually helpful. It does suggest adding another |
| 160 | +lifetime, this time on `Name`. If we do that... |
| 161 | +
|
| 162 | +```bash |
| 163 | +> rustc +1.15.1 .\foo.rs --crate-type=lib |
| 164 | +<snip> |
| 165 | +help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()> |
| 166 | + --> .\foo.rs:10:5 |
| 167 | +``` |
| 168 | +
|
| 169 | +... that's what we already have, compiler! |
| 170 | +
|
| 171 | +This diagnostic was well-intentioned, but when it's wrong, it was *very* wrong, as you can see here. Sometimes |
| 172 | +it wouldn't even suggest valid Rust syntax! Furthermore, more advanced Rust users didn't really need the |
| 173 | +suggestion, but new Rustaceans would take them to heart, and then be led down this bad path. As such, we decided |
| 174 | +that for now, [we should remove the help message entirely]. We may bring it back in the future, but only if we can |
| 175 | +limit false positives. |
| 176 | +
|
| 177 | +[we should remove the help message entirely]: https://github.com/rust-lang/rust/pull/37057 |
| 178 | +
|
| 179 | +In other diagnostic changes, previous versions of Rust would helpfully attempt to suggest fixes for typos: |
| 180 | +
|
| 181 | +```rust |
| 182 | +let foo = 5; |
| 183 | +
|
| 184 | +println!("{}", ffo); |
| 185 | +``` |
| 186 | +
|
| 187 | +Would give this error: |
| 188 | +
|
| 189 | +```text |
| 190 | +error[E0425]: cannot find value `ffo` in this scope |
| 191 | + --> foo.rs:4:20 |
| 192 | + | |
| 193 | +4 | println!("{}", ffo); |
| 194 | + | ^^^ did you mean `foo`? |
| 195 | +``` |
| 196 | +
|
| 197 | +However, this would only happen in certain circumstances: sometimes in local variables, and for fields in |
| 198 | +structs. [This now happens nearly everywhere]. When combined with [some other related improvements], this |
| 199 | +results in a significant improvement in these sorts of diagnostics. |
| 200 | +
|
| 201 | +[This now happens nearly everywhere]: https://github.com/rust-lang/rust/pull/38927 |
| 202 | +[some other related improvements]: https://github.com/rust-lang/rust/pull/38154 |
| 203 | +
|
| 204 | +See the [detailed release notes][notes] for more. |
| 205 | +
|
| 206 | +#### Library stabilizations |
| 207 | +
|
| 208 | +21 new bits of API were stabilized this release: |
| 209 | +
|
| 210 | +* [`VecDeque::truncate`] |
| 211 | +* [`VecDeque::resize`] |
| 212 | +* [`String::insert_str`] |
| 213 | +* [`Duration::checked_add`] |
| 214 | +* [`Duration::checked_sub`] |
| 215 | +* [`Duration::checked_div`] |
| 216 | +* [`Duration::checked_mul`] |
| 217 | +* [`str::replacen`] |
| 218 | +* [`str::repeat`] |
| 219 | +* [`SocketAddr::is_ipv4`] |
| 220 | +* [`SocketAddr::is_ipv6`] |
| 221 | +* [`IpAddr::is_ipv4`] |
| 222 | +* [`IpAddr::is_ipv6`] |
| 223 | +* [`Vec::dedup_by`] |
| 224 | +* [`Vec::dedup_by_key`] |
| 225 | +* [`Result::unwrap_or_default`] |
| 226 | +* [`<*const T>::wrapping_offset`] |
| 227 | +* [`<*mut T>::wrapping_offset`] |
| 228 | +* `CommandExt::creation_flags` |
| 229 | +* [`File::set_permissions`] |
| 230 | +* [`String::split_off`] |
| 231 | +
|
| 232 | +[`<*const T>::wrapping_offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset |
| 233 | +[`<*mut T>::wrapping_offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset |
| 234 | +[`Duration::checked_add`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_add |
| 235 | +[`Duration::checked_div`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_div |
| 236 | +[`Duration::checked_mul`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_mul |
| 237 | +[`Duration::checked_sub`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_sub |
| 238 | +[`File::set_permissions`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.set_permissions |
| 239 | +[`IpAddr::is_ipv4`]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_ipv4 |
| 240 | +[`IpAddr::is_ipv6`]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_ipv6 |
| 241 | +[`Result::unwrap_or_default`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_default |
| 242 | +[`SocketAddr::is_ipv4`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html#method.is_ipv4 |
| 243 | +[`SocketAddr::is_ipv6`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html#method.is_ipv6 |
| 244 | +[`String::insert_str`]: https://doc.rust-lang.org/std/string/struct.String.html#method.insert_str |
| 245 | +[`String::split_off`]: https://doc.rust-lang.org/std/string/struct.String.html#method.split_off |
| 246 | +[`Vec::dedup_by_key`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.dedup_by_key |
| 247 | +[`Vec::dedup_by`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.dedup_by |
| 248 | +[`VecDeque::resize`]: https://doc.rust-lang.org/std/collections/vec_deque/struct.VecDeque.html#method.resize |
| 249 | +[`VecDeque::truncate`]: https://doc.rust-lang.org/std/collections/vec_deque/struct.VecDeque.html#method.truncate |
| 250 | +[`str::repeat`]: https://doc.rust-lang.org/std/primitive.str.html#method.repeat |
| 251 | +[`str::replacen`]: https://doc.rust-lang.org/std/primitive.str.html#method.replacen |
| 252 | +
|
| 253 | +In addition, a number of small improvements to existing functions landed. For example [`writeln!` now has |
| 254 | +a single-argument form], just like `println!` has. This ends up writing only a newline, but is a nice bit |
| 255 | +of symmetry. |
| 256 | +
|
| 257 | +[`writeln!` now has a single-argument form]: https://github.com/rust-lang/rust/pull/38469 |
| 258 | +
|
| 259 | +All structs in the standard library [now implement `Debug`]. |
| 260 | +
|
| 261 | +[now implement `Debug`]: https://github.com/rust-lang/rust/pull/38006 |
| 262 | +
|
| 263 | +When slicing a `&str`, [you'll see better errors]. For example, this code: |
| 264 | +
|
| 265 | +```rust |
| 266 | +&"abcαβγ"[..4] |
| 267 | +``` |
| 268 | +
|
| 269 | +Is incorrect. It generates this error: |
| 270 | +
|
| 271 | +```text |
| 272 | +thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not |
| 273 | +a char boundary; it is inside 'α' (bytes 3..5) of `abcαβγ`' |
| 274 | +``` |
| 275 | +
|
| 276 | +The part after the `;` is new. |
| 277 | +
|
| 278 | +[you'll see better errors]: https://github.com/rust-lang/rust/pull/38066 |
| 279 | +
|
| 280 | +See the [detailed release notes][notes] for more. |
| 281 | +
|
| 282 | +#### Cargo features |
| 283 | +
|
| 284 | +In addition to `cargo check`, Cargo and crates.io have some new polish added. For example, |
| 285 | +[`cargo build`] and [`cargo doc`] now take a `--all` flag for building and documenting every |
| 286 | +crate in your workspace with one command. |
| 287 | +
|
| 288 | +[`cargo build`]: https://github.com/rust-lang/cargo/pull/3511 |
| 289 | +[`cargo doc`]: https://github.com/rust-lang/cargo/pull/3515 |
| 290 | +
|
| 291 | +Cargo now has a [`--version --verbose` flag], mirroring `rustc`. |
| 292 | +
|
| 293 | +[`--version --verbose` flag]: https://github.com/rust-lang/cargo/pull/3604 |
| 294 | +
|
| 295 | +Crates.io now [can show off your TravisCI or AppVeyor badges] on your crate's page. |
| 296 | +
|
| 297 | +[can show off your TravisCI or Appveyor badges]: https://github.com/rust-lang/cargo/pull/3546 |
| 298 | +
|
| 299 | +In addition, both Cargo and crates.io [understand categories]. Unlike keywords, which are free-form, |
| 300 | +categories are curated. In addition, keywords are used for searching, but categories are not. In other |
| 301 | +words, categories are intended to assist browsing, and keywords are intended to assist searching. |
| 302 | +
|
| 303 | +You can browse crates by category [here](https://crates.io/categories). |
| 304 | +
|
| 305 | +[understand categories]: https://github.com/rust-lang/cargo/pull/3301 |
| 306 | +
|
| 307 | +See the [detailed release notes][notes] for more. |
| 308 | +
|
| 309 | +### Contributors to 1.16.0 |
| 310 | +
|
| 311 | +Last release, we introduced [thanks.rust-lang.org](https://thanks.rust-lang.org). |
| 312 | +We have been doing some behind-the-scenes refactoring work to allow for more projects |
| 313 | +than only Rust itself; we're hoping to introduce that in the next release. |
| 314 | +
|
| 315 | +We had 137 individuals contribute to Rust 1.16. |
| 316 | +[Thanks!](https://thanks.rust-lang.org/rust/1.16.0) |
0 commit comments