Release of Rust 1.89.0

The Rust team has announced the release of Rust version 1.89.0. Rust is a programming language designed to help everyone build reliable and efficient software.

If you previously installed Rust via rustup, you can get version 1.89.0 using the following command:

$ rustup update stable

If you have not installed rustup yet, you can obtain it from the corresponding page on our website and view the detailed release notes for 1.89.0.

If you want to help us by testing future versions, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you may encounter!

New features in the stable version 1.89.0

  • Explicit inference of const generic parameters

Rust now supports _ as a parameter for const generic parameters, inferring that value from the surrounding context:

pub fn all_false<const len:="" usize="">() -> [bool; LEN] {  [false; _]}</const>

Similar to the rules where “_” is allowed as a type in signatures, “_” is not allowed as a const generic parameter.

// This is not allowed
pub const fn all_false<const len:="" usize="">() -> [bool; _] {  [false; LEN]}
// Neither is this
pub const ALL_FALSE: [bool; _] = all_false::<10>();</const>

Lint for mismatched lifetime syntax

The omission of lifetimes in function signatures is an ergonomic feature of the Rust language, but it can also be a stumbling block for both novices and experts. This is especially true when inferring lifetimes in types, as there is no visual indication that a lifetime exists:

// The returned type std::slice::Iter has a lifetime,
// but there is no visual indication of this.
//// The omitted lifetime infers the return type's lifetime to be the same as that of scores.
fn items(scores: &[u8]) -> std::slice::Iter<u8> {    scores.iter()}</u8>

Code like this will now default to producing a warning:

warning: hiding a lifetime that's elided elsewhere is confusing --> src/lib.rs:1:18  |
1 | fn items(scores: &[u8]) -> std::slice::Iter<u8> {  |
                  ^^^^^     -------------------- the same lifetime is hidden here  |
                  |  |                  the lifetime is elided here  |
  = help: use `'_` for type paths  |
1 | fn items(scores: &[u8]) -> std::slice::Iter<'_, u8> {  |
</u8>

We first attempted to improve this situation in 2018 as part of the rust_2018_idioms lint group, but strong feedback regarding the elided_lifetimes_in_paths lint indicated that it was too blunt, as it warned those lifetimes that were irrelevant to understanding the function:

use std::fmt;
struct Greeting;
impl fmt::Display for Greeting {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // -----^^^^^^^^^ expected lifetime parameter
        // Knowing that Formatter has a lifetime does not help the programmer
        "howdy".fmt(f)
    }
}

We then realized that the confusion we wanted to eliminate occurs when both of the following situations happen simultaneously:

  • The lifetime omission inference rules connect input lifetimes to output lifetimes

  • There is no visual indication that a lifetime exists from a syntactical perspective

There are two Rust syntaxes that indicate the existence of a lifetime: & and ‘, where ‘ is further divided into inferred lifetime _ and named lifetime ‘a. When types use named lifetimes, lifetime omission does not infer lifetimes for that type. Using these criteria, we can construct three groups:

Self-evident lifetimes

Allow lifetime omission to infer lifetimes

Examples

No

Yes

ContainsLifetime

Yes

Yes

&T, &’ T, ContainsLifetime<‘>

Yes

No

&’a T, ContainsLifetime<‘a>

The mismatched_lifetime_syntaxes lint checks whether the input and output lifetimes belong to the same group. For the initial motivating example above, &[u8] belongs to the second group, while std::slice::Iter belongs to the first group. We say that the lifetimes in the first group are hidden.

Since the input and output lifetimes belong to different groups, the lint will warn about this function, reducing confusion about when values have non-visually obvious meaningful lifetimes.

The mismatched_lifetime_syntaxes lint replaces the elided_named_lifetimes lint, which did something similar specifically for named lifetimes.

Future work on the elided_lifetimes_in_paths lint plans to break it down into more focused sub-lints, aiming to eventually warn about parts of it.

  • More x86 target features

The target_feature attribute now supports sha512, sm3, sm4, kl, and widekl target features on x86. Additionally, many avx512 inline functions and target features are supported on x86:

#[target_feature(enable = "avx512bw")]
pub fn cool_simd_code(/* .. */) -> /* ... */ {    /* ... */}
  • Documentation tests for cross-platform compilation

Now, when running cargo test –doc –target other_target, documentation tests will be tested. This may lead to some breakage, as documentation tests that would have failed are now being tested.

You can disable failing tests (documentation) by using the ignore- annotation in documentation tests:

/// ```ignore-x86_64  /// panic!("something")  /// ```
pub fn my_function() { }
  • i128 and u128 in extern “C” functions

i128 and u128 no longer trigger improper_ctypes_definitions lint, meaning these types can be used in extern “C” functions without warnings. However, note the following:

  • When available, Rust types are ABI and layout compatible with (unsigned) __int128 in C.

  • On platforms where __int128 is not available, i128 and u128 may not align with any C type.

  • i128 may not be compatible with _BitInt(128) on any platform, as _BitInt(128) and __int128 may not have the same ABI (e.g., on x86-64).

This is the final follow-up to last year’s layout change: https://blog.rust-lang.org/2024/03/30/i128-layout-update.

  • Downgrading x86_64-apple-darwin to Tier 2 with host tools

GitHub will soon stop providing free macOS x86_64 runners for public repositories. Apple has also announced their plans to stop supporting the x86_64 architecture.

In light of these changes, the Rust project is downgrading the x86_64-apple-darwin target from Tier 1 with host tools to Tier 2 with host tools. This means that this target (including tools like rustc and cargo) will be guaranteed to build, but it will not be guaranteed to pass our automated test suite.

We expect that the RFC for downgrading to Tier 2 with host tools will be accepted between Rust versions 1.89 and 1.90, meaning Rust 1.89 will be the last Rust version for x86_64-apple-darwin as a Tier 1 target.

This change will not have an immediate impact on users. The standard library and compiler builds will still be distributed by the Rust project for use via rustup or other installation methods, while the target remains at Tier 2. Over time, reduced test coverage for this target may lead to issues or incompatibilities without notice.

  • Standard C ABI compliance on the wasm32-unknown-unknown target

Extern “C” functions on the wasm32-unknown-unknown target now have a standard-compliant ABI. For more information, see this blog post: https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown.

  1. Platform support

  2. x86_64-apple-darwin is being downgraded to Tier 2 with host tools

  3. New Tier-3 targets loongarch32-unknown-none and loongarch32-unknown-none-softfloat added

For more information on Rust’s tiered platform support, please refer to Rust’s platform support page.

Leave a Comment