Skip to content

Commit 37c317d

Browse files
1.87.0 announcement
1 parent 13434fc commit 37c317d

File tree

1 file changed

+252
-0
lines changed

1 file changed

+252
-0
lines changed

content/Rust-1.87.0.md

+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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

Comments
 (0)