-
Notifications
You must be signed in to change notification settings - Fork 298
1.87.0 announcement #1604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
1.87.0 announcement #1604
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,251 @@ | ||||||||||
+++ | ||||||||||
path = "2025/05/15/Rust-1.87.0" | ||||||||||
title = "Announcing Rust 1.87.0" | ||||||||||
authors = ["The Rust Release Team"] | ||||||||||
aliases = [ | ||||||||||
"2025/05/15/Rust-1.87.0.html", | ||||||||||
"releases/1.87.0", | ||||||||||
] | ||||||||||
|
||||||||||
[extra] | ||||||||||
release = true | ||||||||||
+++ | ||||||||||
|
||||||||||
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. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be awesome to embed a photo of the celebration room filled with people directly into the blog post :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'd also be good to make clear that the 1.87.0 release itself isn't a special release; just that the day is. There will probably be some press coverage, and I don't think they should report the specific features of 1.87.0 itself as the big 10 year milestone. Happy to think of some text for this. Probably a separate paragraph at the top or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was planning on doing that for the posts on social media. (Which I can just do from my phone.) Might be tricky to do that in the blog post, though, to do that quickly on stage. |
||||||||||
|
||||||||||
If you have a previous version of Rust installed via `rustup`, you can get 1.87.0 with: | ||||||||||
|
||||||||||
``` | ||||||||||
$ rustup update stable | ||||||||||
``` | ||||||||||
|
||||||||||
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). | ||||||||||
|
||||||||||
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! | ||||||||||
|
||||||||||
## What's in 1.87.0 stable | ||||||||||
|
||||||||||
### Anonymous pipes | ||||||||||
|
||||||||||
1.87 adds access to anonymous pipes to the standard library. This includes | ||||||||||
integration with `std::process::Command`'s input/output methods. For example, | ||||||||||
joining the stdout and stderr streams into one is now relatively | ||||||||||
straightforward, as shown below, while it used to require either extra threads | ||||||||||
or platform-specific functions. | ||||||||||
|
||||||||||
```rust | ||||||||||
use std::process::Command; | ||||||||||
use std::io::Read; | ||||||||||
|
||||||||||
let (mut recv, send) = std::io::pipe()?; | ||||||||||
|
||||||||||
let mut command = Command::new("path/to/bin") | ||||||||||
// Both stdout and stderr will write to the same pipe, combining the two. | ||||||||||
.stdout(send.try_clone()?) | ||||||||||
.stderr(send) | ||||||||||
.spawn()?; | ||||||||||
|
||||||||||
let mut output = Vec::new(); | ||||||||||
recv.read_to_end(&mut output)?; | ||||||||||
|
||||||||||
// It's important that we read from the pipe before the process exits, to avoid | ||||||||||
// filling the OS buffers if the program emits too much output. | ||||||||||
assert!(command.wait()?.success()); | ||||||||||
``` | ||||||||||
|
||||||||||
### Safe architecture intrinsics | ||||||||||
|
||||||||||
Most `std::arch` intrinsics that are unsafe only due to requiring target | ||||||||||
features to be enabled are now callable in safe code that has those features | ||||||||||
enabled. For example, the following toy program which implements summing an array using | ||||||||||
manual intrinsics can now use safe code for the core loop. | ||||||||||
|
||||||||||
```rust | ||||||||||
#![forbid(unsafe_op_in_unsafe_fn)] | ||||||||||
|
||||||||||
use std::arch::x86_64::*; | ||||||||||
|
||||||||||
fn sum(slice: &[u32]) -> u32 { | ||||||||||
#[cfg(target_arch = "x86_64")] | ||||||||||
{ | ||||||||||
if is_x86_feature_detected!("avx2") { | ||||||||||
// SAFETY: We have detected the feature is enabled at runtime, | ||||||||||
// so it's safe to call this function. | ||||||||||
return unsafe { sum_avx2(slice) }; | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
slice.iter().sum() | ||||||||||
} | ||||||||||
|
||||||||||
#[target_feature(enable = "avx2")] | ||||||||||
#[cfg(target_arch = "x86_64")] | ||||||||||
fn sum_avx2(slice: &[u32]) -> u32 { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe there's a better (shorter?) example that can be fully safe code now except the function call? Something like crc32 perhaps that operates on just u64 values and so doesn't need the align/transmutes? |
||||||||||
// SAFETY: __m256i and u32 have the same validity. | ||||||||||
let (prefix, middle, tail) = unsafe { slice.align_to::<__m256i>() }; | ||||||||||
|
||||||||||
let mut sum = prefix.iter().sum::<u32>(); | ||||||||||
sum += tail.iter().sum::<u32>(); | ||||||||||
|
||||||||||
// Core loop is now fully safe code in 1.87, because the intrinsics require | ||||||||||
// matching target features (avx2) to the function definition. | ||||||||||
let mut base = _mm256_setzero_si256(); | ||||||||||
for e in middle.iter() { | ||||||||||
base = _mm256_add_epi32(base, *e); | ||||||||||
} | ||||||||||
|
||||||||||
// SAFETY: __m256i and u32 have the same validity. | ||||||||||
let base: [u32; 8] = unsafe { std::mem::transmute(base) }; | ||||||||||
sum += base.iter().sum::<u32>(); | ||||||||||
|
||||||||||
sum | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
### `asm!` jumps to Rust code | ||||||||||
|
||||||||||
Inline assembly (`asm!`) can now jump to labeled blocks within Rust code. This | ||||||||||
enables more flexible low-level programming, such as implementing optimized | ||||||||||
control flow in OS kernels or interacting with hardware more efficiently. | ||||||||||
|
||||||||||
- The `asm!` macro now supports a label operand, which acts as a jump target. | ||||||||||
- The label must be a block expression with a return type of `()` or `!`. | ||||||||||
- The block executes when jumped to, and execution continues after the `asm!` block. | ||||||||||
- Using output and label operands in the same `asm!` invocation remains [unstable](https://github.com/rust-lang/rust/issues/119364). | ||||||||||
|
||||||||||
```rust | ||||||||||
unsafe { | ||||||||||
asm!( | ||||||||||
"jmp {}", | ||||||||||
label { | ||||||||||
println!("Jumped from asm!"); | ||||||||||
} | ||||||||||
); | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
For more details, please consult the [reference](https://doc.rust-lang.org/nightly/reference/inline-assembly.html#r-asm.operand-type.supported-operands.label). | ||||||||||
|
||||||||||
### Precise capturing (`+ use<...>`) in `impl Trait` in trait definitions | ||||||||||
|
||||||||||
This release stabilizes specifying the specific captured generic types and | ||||||||||
lifetimes in trait definitions using `impl Trait` return types. This allows | ||||||||||
using this feature in trait definitions, expanding on the stabilization for | ||||||||||
non-trait functions in | ||||||||||
[1.82](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/#precise-capturing-use-syntax). | ||||||||||
|
||||||||||
Some example desugarings: | ||||||||||
|
||||||||||
```rust | ||||||||||
trait Foo { | ||||||||||
fn method<'a>(&'a self) -> impl Sized; | ||||||||||
|
||||||||||
// ... desugars to something like: | ||||||||||
type Implicit1<'a>: Sized; | ||||||||||
fn method_desugared<'a>(&'a self) -> Self::Implicit1<'a>; | ||||||||||
|
||||||||||
// ... whereas with precise capturing ... | ||||||||||
fn precise<'a>(&'a self) -> impl Sized + use<Self>; | ||||||||||
|
||||||||||
// ... desugars to something like: | ||||||||||
type Implicit2: Sized; | ||||||||||
fn precise_desugared<'a>(&'a self) -> Self::Implicit2; | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
### Stabilized APIs | ||||||||||
|
||||||||||
- [`Vec::extract_if`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.extract_if) | ||||||||||
- [`vec::ExtractIf`](https://doc.rust-lang.org/nightly/std/vec/struct.ExtractIf.html) | ||||||||||
- [`LinkedList::extract_if`](https://doc.rust-lang.org/nightly/std/collections/struct.LinkedList.html#method.extract_if) | ||||||||||
- [`linked_list::ExtractIf`](https://doc.rust-lang.org/nightly/std/collections/linked_list/struct.ExtractIf.html) | ||||||||||
- [`<[T]>::split_off`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off) | ||||||||||
- [`<[T]>::split_off_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_mut) | ||||||||||
- [`<[T]>::split_off_first`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_first) | ||||||||||
- [`<[T]>::split_off_first_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_first_mut) | ||||||||||
- [`<[T]>::split_off_last`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_last) | ||||||||||
- [`<[T]>::split_off_last_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_off_last_mut) | ||||||||||
- [`String::extend_from_within`](https://doc.rust-lang.org/stable/alloc/string/struct.String.html#method.extend_from_within) | ||||||||||
- [`os_str::Display`](https://doc.rust-lang.org/nightly/std/ffi/os_str/struct.Display.html) | ||||||||||
- [`OsString::display`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsString.html#method.display) | ||||||||||
- [`OsStr::display`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsStr.html#method.display) | ||||||||||
- [`io::pipe`](https://doc.rust-lang.org/nightly/std/io/fn.pipe.html) | ||||||||||
- [`io::PipeReader`](https://doc.rust-lang.org/nightly/std/io/struct.PipeReader.html) | ||||||||||
- [`io::PipeWriter`](https://doc.rust-lang.org/nightly/std/io/struct.PipeWriter.html) | ||||||||||
- [`impl From<PipeReader> for OwnedHandle`](https://doc.rust-lang.org/nightly/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeReader%3E-for-OwnedHandle) | ||||||||||
- [`impl From<PipeWriter> for OwnedHandle`](https://doc.rust-lang.org/nightly/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeWriter%3E-for-OwnedHandle) | ||||||||||
- [`impl From<PipeReader> for Stdio`](https://doc.rust-lang.org/nightly/std/process/struct.Stdio.html) | ||||||||||
- [`impl From<PipeWriter> for Stdio`](https://doc.rust-lang.org/nightly/std/process/struct.Stdio.html#impl-From%3CPipeWriter%3E-for-Stdio) | ||||||||||
- [`impl From<PipeReader> for OwnedFd`](https://doc.rust-lang.org/nightly/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeReader%3E-for-OwnedFd) | ||||||||||
- [`impl From<PipeWriter> for OwnedFd`](https://doc.rust-lang.org/nightly/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeWriter%3E-for-OwnedFd) | ||||||||||
- [`Box<MaybeUninit<T>>::write`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.write) | ||||||||||
- [`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) | ||||||||||
|
||||||||||
These APIs are now stable in const contexts: | ||||||||||
|
||||||||||
- [`<*const T>::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.offset_from_unsigned) | ||||||||||
- [`<*const T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.byte_offset_from_unsigned) | ||||||||||
- [`<*mut T>::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.offset_from_unsigned-1) | ||||||||||
- [`<*mut T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.byte_offset_from_unsigned-1) | ||||||||||
- [`NonNull::offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.offset_from_unsigned) | ||||||||||
- [`NonNull::byte_offset_from_unsigned`](https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.byte_offset_from_unsigned) | ||||||||||
- [`<uN>::cast_signed`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.cast_signed) | ||||||||||
- [`NonZero::<uN>::cast_signed`](https://doc.rust-lang.org/nightly/std/num/struct.NonZero.html#method.cast_signed-5). | ||||||||||
- [`<iN>::cast_signed`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.cast_signed). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- [`NonZero::<iN>::cast_unsigned`](https://doc.rust-lang.org/nightly/std/num/struct.NonZero.html#method.cast_unsigned-5). | ||||||||||
- [`<uN>::is_multiple_of`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.is_multiple_of) | ||||||||||
- [`<uN>::unbounded_shl`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.unbounded_shl) | ||||||||||
- [`<uN>::unbounded_shr`](https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.unbounded_shr) | ||||||||||
- [`<iN>::unbounded_shl`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.unbounded_shl) | ||||||||||
- [`<iN>::unbounded_shr`](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.unbounded_shr) | ||||||||||
- [`<str>::from_utf8`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8) | ||||||||||
- [`<str>::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_mut) | ||||||||||
- [`<str>::from_utf8_unchecked`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_unchecked) | ||||||||||
- [`<str>::from_utf8_unchecked_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_unchecked_mut) | ||||||||||
- [`<[T]>::copy_from_slice`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.copy_from_slice) | ||||||||||
- [`SocketAddr::set_ip`](https://doc.rust-lang.org/nightly/std/net/enum.SocketAddr.html#method.set_ip) | ||||||||||
- [`SocketAddr::set_port`](https://doc.rust-lang.org/nightly/std/net/enum.SocketAddr.html#method.set_port), | ||||||||||
- [`SocketAddrV4::set_ip`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV4.html#method.set_ip) | ||||||||||
- [`SocketAddrV4::set_port`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV4.html#method.set_port), | ||||||||||
- [`SocketAddrV6::set_ip`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_ip) | ||||||||||
- [`SocketAddrV6::set_port`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_port) | ||||||||||
- [`SocketAddrV6::set_flowinfo`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_flowinfo) | ||||||||||
- [`SocketAddrV6::set_scope_id`](https://doc.rust-lang.org/nightly/std/net/struct.SocketAddrV6.html#method.set_scope_id) | ||||||||||
- [`char::is_digit`](https://doc.rust-lang.org/nightly/std/primitive.char.html#method.is_digit) | ||||||||||
- [`char::is_whitespace`](https://doc.rust-lang.org/nightly/std/primitive.char.html#method.is_whitespace) | ||||||||||
- [`<iN>:midpoint`](https://doc.rust-lang.org/std/primitive.isize.html#method.midpoint) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- [`<[[T; N]]>::as_flattened`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_flattened) | ||||||||||
- [`<[[T; N]]>::as_flattened_mut`](https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_flattened_mut) | ||||||||||
- [`<str>::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/primitive.str.html#method.from_utf8_mut) | ||||||||||
- [`core::str::from_utf8_mut`](https://doc.rust-lang.org/nightly/std/str/fn.from_utf8_mut.html) | ||||||||||
- [`String::into_bytes`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.into_bytes) | ||||||||||
- [`String::as_str`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_str) | ||||||||||
- [`String::capacity`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.capacity) | ||||||||||
- [`String::as_bytes`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_bytes) | ||||||||||
- [`String::len`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.len) | ||||||||||
- [`String::is_empty`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.is_empty) | ||||||||||
- [`String::as_mut_str`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_mut_str) | ||||||||||
- [`String::as_mut_vec`](https://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_mut_vec) | ||||||||||
- [`Vec::as_ptr`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_ptr) | ||||||||||
- [`Vec::as_slice`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_slice) | ||||||||||
- [`Vec::capacity`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.capacity) | ||||||||||
- [`Vec::len`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.len) | ||||||||||
- [`Vec::is_empty`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.is_empty) | ||||||||||
- [`Vec::as_mut_slice`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_mut_slice) | ||||||||||
- [`Vec::as_mut_ptr`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_mut_ptr) | ||||||||||
|
||||||||||
### `i586-pc-windows-msvc` target removal | ||||||||||
|
||||||||||
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. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
|
||||||||||
|
||||||||||
All users currently targeting `i586-pc-windows-msvc` should migrate to `i686-pc-windows-msvc`. | ||||||||||
|
||||||||||
You can check the [Major Change Proposal](https://github.com/rust-lang/compiler-team/issues/840) for more information. | ||||||||||
|
||||||||||
### Other changes | ||||||||||
|
||||||||||
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). | ||||||||||
|
||||||||||
## Contributors to 1.87.0 | ||||||||||
|
||||||||||
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/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.