|
| 1 | ++++ |
| 2 | +path = "2025/05/15/Rust-1.87.0" |
| 3 | +title = "Announcing Rust 1.87.0" |
| 4 | +authors = ["The Rust Release Team"] |
| 5 | +aliases = [ |
| 6 | + "2025/05/15/Rust-1.87.0.html", |
| 7 | + "releases/1.87.0", |
| 8 | +] |
| 9 | + |
| 10 | +[extra] |
| 11 | +release = true |
| 12 | ++++ |
| 13 | + |
| 14 | +The Rust team is happy to announce a new version of Rust, 1.87.0. Rust is a programming language empowering everyone to build reliable and efficient software. |
| 15 | + |
| 16 | +If you have a previous version of Rust installed via `rustup`, you can get 1.87.0 with: |
| 17 | + |
| 18 | +``` |
| 19 | +$ rustup update stable |
| 20 | +``` |
| 21 | + |
| 22 | +If you don't have it already, you can [get `rustup`](https://www.rust-lang.org/install.html) from the appropriate page on our website, and check out the [detailed release notes for 1.87.0](https://doc.rust-lang.org/stable/releases.html#version-1870-2025-04-03). |
| 23 | + |
| 24 | +If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (`rustup default beta`) or the nightly channel (`rustup default nightly`). Please [report](https://github.com/rust-lang/rust/issues/new/choose) any bugs you might come across! |
| 25 | + |
| 26 | +## What's in 1.87.0 stable |
| 27 | + |
| 28 | +### Anonymous pipes |
| 29 | + |
| 30 | +1.87 adds access to anonymous pipes to the standard library. This includes |
| 31 | +integration with `std::process::Command`'s input/output methods. |
| 32 | + |
| 33 | +As a toy example, this program uses pipes to stream data into `cat` and read the output: |
| 34 | + |
| 35 | +```rust |
| 36 | +use std::process::Command; |
| 37 | +use std::io::{pipe, Read, Write}; |
| 38 | + |
| 39 | +let (ping_rx, mut ping_tx) = pipe()?; |
| 40 | +let (mut pong_rx, pong_tx) = pipe()?; |
| 41 | + |
| 42 | +// Spawn a process that echoes its input. |
| 43 | +let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; |
| 44 | + |
| 45 | +ping_tx.write_all(b"hello")?; |
| 46 | +// Close to unblock echo_server's reader. |
| 47 | +drop(ping_tx); |
| 48 | + |
| 49 | +let mut buf = String::new(); |
| 50 | +// Block until echo_server's writer is closed. |
| 51 | +pong_rx.read_to_string(&mut buf)?; |
| 52 | +assert_eq!(&buf, "hello"); |
| 53 | + |
| 54 | +echo_server.wait()?; |
| 55 | +``` |
| 56 | + |
| 57 | +### Safe architecture intrinsics |
| 58 | + |
| 59 | +Most `std::arch` intrinsics that are unsafe only due to requiring target |
| 60 | +features to be enabled are now callable in safe code that has those features |
| 61 | +enabled. For example, the following toy program which implements summing an array using |
| 62 | +manual intrinsics can now use safe code for the core loop. |
| 63 | + |
| 64 | +```rust |
| 65 | +#![forbid(unsafe_op_in_unsafe_fn)] |
| 66 | + |
| 67 | +use std::arch::x86_64::*; |
| 68 | + |
| 69 | +fn sum(slice: &[u32]) -> u32 { |
| 70 | + #[cfg(target_arch = "x86_64")] |
| 71 | + { |
| 72 | + if is_x86_feature_detected!("avx2") { |
| 73 | + // SAFETY: We have detected the feature is enabled at runtime, |
| 74 | + // so it's safe to call this function. |
| 75 | + return unsafe { sum_avx2(slice) }; |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + slice.iter().sum() |
| 80 | +} |
| 81 | + |
| 82 | +#[target_feature(enable = "avx2")] |
| 83 | +#[cfg(target_arch = "x86_64")] |
| 84 | +fn sum_avx2(slice: &[u32]) -> u32 { |
| 85 | + // SAFETY: __m256i and u32 have the same validity. |
| 86 | + let (prefix, middle, tail) = unsafe { slice.align_to::<__m256i>() }; |
| 87 | + |
| 88 | + let mut sum = prefix.iter().sum::<u32>(); |
| 89 | + sum += tail.iter().sum::<u32>(); |
| 90 | + |
| 91 | + // Core loop is now fully safe code in 1.87, because the intrinsics require |
| 92 | + // matching target features (avx2) to the function definition. |
| 93 | + let mut base = _mm256_setzero_si256(); |
| 94 | + for e in middle.iter() { |
| 95 | + base = _mm256_add_epi32(base, *e); |
| 96 | + } |
| 97 | + |
| 98 | + // SAFETY: __m256i and u32 have the same validity. |
| 99 | + let base: [u32; 8] = unsafe { std::mem::transmute(base) }; |
| 100 | + sum += base.iter().sum::<u32>(); |
| 101 | + |
| 102 | + sum |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +### `asm!` jumps to Rust code |
| 107 | + |
| 108 | +Inline assembly (`asm!`) can now jump to labeled blocks within Rust code. This |
| 109 | +enables more flexible low-level programming, such as implementing optimized |
| 110 | +control flow in OS kernels or interacting with hardware more efficiently. |
| 111 | + |
| 112 | +- The `asm!` macro now supports a label operand, which acts as a jump target. |
| 113 | +- The label must be a block expression with a return type of `()` or `!`. |
| 114 | +- The block executes when jumped to, and execution continues after the `asm!` block. |
| 115 | +- Using output and label operands in the same `asm!` invocation remains [unstable](https://github.com/rust-lang/rust/issues/119364). |
| 116 | + |
| 117 | +```rust |
| 118 | +unsafe { |
| 119 | + asm!( |
| 120 | + "jmp {}", |
| 121 | + label { |
| 122 | + println!("Jumped from asm!"); |
| 123 | + } |
| 124 | + ); |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +For more details, please consult the [reference](https://doc.rust-lang.org/nightly/reference/inline-assembly.html#r-asm.operand-type.supported-operands.label). |
| 129 | + |
| 130 | +### Precise capturing (`+ use<...>`) in `impl Trait` in trait definitions |
| 131 | + |
| 132 | +This release stabilizes specifying the specific captured generic types and |
| 133 | +lifetimes in trait definitions using `impl Trait` return types. This allows |
| 134 | +using this feature in trait definitions, expanding on the stabilization for |
| 135 | +non-trait functions in |
| 136 | +[1.82](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/#precise-capturing-use-syntax). |
| 137 | + |
| 138 | +Some example desugarings: |
| 139 | + |
| 140 | +```rust |
| 141 | +trait Foo { |
| 142 | + fn method<'a>(&'a self) -> impl Sized; |
| 143 | + |
| 144 | + // ... desugars to something like: |
| 145 | + type Implicit1<'a>: Sized; |
| 146 | + fn method_desugared<'a>(&'a self) -> Self::Implicit1<'a>; |
| 147 | + |
| 148 | + // ... whereas with precise capturing ... |
| 149 | + fn precise<'a>(&'a self) -> impl Sized + use<Self>; |
| 150 | + |
| 151 | + // ... desugars to something like: |
| 152 | + type Implicit2: Sized; |
| 153 | + fn precise_desugared<'a>(&'a self) -> Self::Implicit2; |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +### Stabilized APIs |
| 158 | + |
| 159 | +- [`Vec::extract_if`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.extract_if) |
| 160 | +- [`vec::ExtractIf`](https://doc.rust-lang.org/nightly/std/vec/struct.ExtractIf.html) |
| 161 | +- [`LinkedList::extract_if`](https://doc.rust-lang.org/nightly/std/collections/struct.LinkedList.html#method.extract_if) |
| 162 | +- [`linked_list::ExtractIf`](https://doc.rust-lang.org/nightly/std/collections/linked_list/struct.ExtractIf.html) |
| 163 | +- [`<[T]>::split_off`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off) |
| 164 | +- [`<[T]>::split_off_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_mut) |
| 165 | +- [`<[T]>::split_off_first`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_first) |
| 166 | +- [`<[T]>::split_off_first_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_first_mut) |
| 167 | +- [`<[T]>::split_off_last`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_last) |
| 168 | +- [`<[T]>::split_off_last_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_last_mut) |
| 169 | +- [`String::extend_from_within`](https://doc.rust-lang.org/stable/alloc/string/struct.String.html#method.extend_from_within) |
| 170 | +- [`os_str::Display`](https://doc.rust-lang.org/nightly/std/ffi/os_str/struct.Display.html) |
| 171 | +- [`OsString::display`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsString.html#method.display) |
| 172 | +- [`OsStr::display`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsStr.html#method.display) |
| 173 | +- [`io::pipe`](https://doc.rust-lang.org/nightly/std/io/fn.pipe.html) |
| 174 | +- [`io::PipeReader`](https://doc.rust-lang.org/nightly/std/io/struct.PipeReader.html) |
| 175 | +- [`io::PipeWriter`](https://doc.rust-lang.org/nightly/std/io/struct.PipeWriter.html) |
| 176 | +- [`impl From<PipeReader> for OwnedHandle`](https://doc.rust-lang.org/nightly/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeReader%3E-for-OwnedHandle) |
| 177 | +- [`impl From<PipeWriter> for OwnedHandle`](https://doc.rust-lang.org/nightly/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeWriter%3E-for-OwnedHandle) |
| 178 | +- [`impl From<PipeReader> for Stdio`](https://doc.rust-lang.org/nightly/std/process/struct.Stdio.html) |
| 179 | +- [`impl From<PipeWriter> for Stdio`](https://doc.rust-lang.org/nightly/std/process/struct.Stdio.html#impl-From%3CPipeWriter%3E-for-Stdio) |
| 180 | +- [`impl From<PipeReader> for OwnedFd`](https://doc.rust-lang.org/nightly/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeReader%3E-for-OwnedFd) |
| 181 | +- [`impl From<PipeWriter> for OwnedFd`](https://doc.rust-lang.org/nightly/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeWriter%3E-for-OwnedFd) |
| 182 | +- [`Box<MaybeUninit<T>>::write`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.write) |
| 183 | +- [`impl TryFrom<Vec<u8>> for String`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#impl-TryFrom%3CVec%3Cu8%3E%3E-for-String) |
| 184 | + |
| 185 | +These APIs are now stable in const contexts: |
| 186 | + |
| 187 | +- [`<*const T>::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.offset_from_unsigned) |
| 188 | +- [`<*const T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.byte_offset_from_unsigned) |
| 189 | +- [`<*mut T>::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.offset_from_unsigned-1) |
| 190 | +- [`<*mut T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.byte_offset_from_unsigned-1) |
| 191 | +- [`NonNull::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.offset_from_unsigned) |
| 192 | +- [`NonNull::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.byte_offset_from_unsigned) |
| 193 | +- [`<uN>::cast_signed`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.cast_signed) |
| 194 | +- [`NonZero::<uN>::cast_signed`](https://doc.rust-lang.org/nightly/std/num/struct.NonZero.html#method.cast_signed-5). |
| 195 | +- [`<iN>::cast_signed`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.cast_signed). |
| 196 | +- [`NonZero::<iN>::cast_unsigned`](https://doc.rust-lang.org/nightly/std/num/struct.NonZero.html#method.cast_unsigned-5). |
| 197 | +- [`<uN>::is_multiple_of`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.is_multiple_of) |
| 198 | +- [`<uN>::unbounded_shl`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.unbounded_shl) |
| 199 | +- [`<uN>::unbounded_shr`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.unbounded_shr) |
| 200 | +- [`<iN>::unbounded_shl`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.unbounded_shl) |
| 201 | +- [`<iN>::unbounded_shr`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.unbounded_shr) |
| 202 | +- [`<str>::from_utf8`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8) |
| 203 | +- [`<str>::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_mut) |
| 204 | +- [`<str>::from_utf8_unchecked`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_unchecked) |
| 205 | +- [`<str>::from_utf8_unchecked_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_unchecked_mut) |
| 206 | +- [`<[T]>::copy_from_slice`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.copy_from_slice) |
| 207 | +- [`SocketAddr::set_ip`](https://doc.rust-lang.org/nightly/std/net/enum.SocketAddr.html#method.set_ip) |
| 208 | +- [`SocketAddr::set_port`](https://doc.rust-lang.org/nightly/std/net/enum.SocketAddr.html#method.set_port), |
| 209 | +- [`SocketAddrV4::set_ip`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV4.html#method.set_ip) |
| 210 | +- [`SocketAddrV4::set_port`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV4.html#method.set_port), |
| 211 | +- [`SocketAddrV6::set_ip`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_ip) |
| 212 | +- [`SocketAddrV6::set_port`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_port) |
| 213 | +- [`SocketAddrV6::set_flowinfo`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_flowinfo) |
| 214 | +- [`SocketAddrV6::set_scope_id`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_scope_id) |
| 215 | +- [`char::is_digit`](https://doc.rust-lang.org/nightly/std/primitive.char.html#method.is_digit) |
| 216 | +- [`char::is_whitespace`](https://doc.rust-lang.org/nightly/std/primitive.char.html#method.is_whitespace) |
| 217 | +- [`<iN>:midpoint`](https://doc.rust-lang.org/std/primitive.isize.html#method.midpoint) |
| 218 | +- [`<[[T; N]]>::as_flattened`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_flattened) |
| 219 | +- [`<[[T; N]]>::as_flattened_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_flattened_mut) |
| 220 | +- [`<str>::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_mut) |
| 221 | +- [`core::str::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/str/fn.from_utf8_mut.html) |
| 222 | +- [`String::into_bytes`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.into_bytes) |
| 223 | +- [`String::as_str`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_str) |
| 224 | +- [`String::capacity`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.capacity) |
| 225 | +- [`String::as_bytes`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_bytes) |
| 226 | +- [`String::len`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.len) |
| 227 | +- [`String::is_empty`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.is_empty) |
| 228 | +- [`String::as_mut_str`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_mut_str) |
| 229 | +- [`String::as_mut_vec`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_mut_vec) |
| 230 | +- [`Vec::as_ptr`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_ptr-1) |
| 231 | +- [`Vec::as_slice`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_slice) |
| 232 | +- [`Vec::capacity`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.capacity) |
| 233 | +- [`Vec::len`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.len) |
| 234 | +- [`Vec::is_empty`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.is_empty) |
| 235 | +- [`Vec::as_mut_slice`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_mut_slice) |
| 236 | +- [`Vec::as_mut_ptr`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_mut_ptr) |
| 237 | + |
| 238 | +### `i586-pc-windows-msvc` target removal |
| 239 | + |
| 240 | +The tier-2 target `i586-pc-windows-msvc` has been removed. Its difference to the much more popular `i686-pc-windows-msvc` is that it does not require SSE2 instruction support, but Windows 10, the minimum required OS version of all `windows` targets (except the `win7` targets), requires SSE2 instructions itself. |
| 241 | + |
| 242 | +All users currently targeting `i586-pc-windows-msvc` should migrate to `i686-pc-windows-msvc`. |
| 243 | + |
| 244 | +You can check the [Major Change Proposal](https://github.com/rust-lang/compiler-team/issues/840) for more information. |
| 245 | + |
| 246 | +### Other changes |
| 247 | + |
| 248 | +Check out everything that changed in [Rust](https://github.com/rust-lang/rust/releases/tag/1.87.0), [Cargo](https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-187-2025-05-15), and [Clippy](https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#rust-187). |
| 249 | + |
| 250 | +## Contributors to 1.87.0 |
| 251 | + |
| 252 | +Many people came together to create Rust 1.87.0. We couldn't have done it without all of you. [Thanks!](https://thanks.rust-lang.org/rust/1.87.0/) |
0 commit comments