以下の内容はhttps://aznhe21.hatenablog.com/entry/2026/01/23/rust-1.93より取得しました。


Rust 1.93を早めに深掘り

本日1/23(金)にリリースされたRust 1.93の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。

この記事は原文の理解や和訳のために一部生成AIを使用していますが、すべて筆者の考えに基づく文章で構成しており、 漫然と生成AIを使用しているものではありません。

ピックアップ

個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。

内部可変性のある型を定数で扱うと警告が出るようになった

std::sync::Onceなどは&selfを受け取りつつも内部状態を変更することが出来ます。 これを内部可変性と言いますが、こういった型を定数として扱うと毎回コピーされてしまうため予期せぬ動作に繋がります。

Rust 1.93ではこの使い方に警告が出るようになりました。

use std::sync::Once;

// 正しくはstatic
const INIT: Once = Once::new();

fn main() {
    INIT.call_once(|| {
//  ^---
//  |
//__`INIT` is a interior mutable `const` item of type `std::sync::Once`
        println!("一度だけ表示したいが");
    });
    INIT.call_once(|| {
//  ^---
//  |
//__`INIT` is a interior mutable `const` item of type `std::sync::Once`
        println!("こっちも表示される");
    });
}

クロージャで直接値をフォーマットできるようになった

Displayを実装していない型や実装している型の表示の仕方を変えたい場合、 一時的に文字列を経由する無駄を許容するか、それ専用に型を実装する必要があり面倒でした。

Rust 1.93からはstd::fmt::from_fnが使えるようになり、クロージャを渡すことでDisplayDebugを簡易に実装することができるようになりました。

例えばtracingクレートではtracing::info!(value = %val, "message)のようにすることでvalを文字列化したものをイベントに紐付けることが出来ます。 ただしvalDisplayを実装している必要がありますが、ここでfmt::from_fntを使うと簡単に出来ます。

// Cargo.tomlにtracingとtracing-subscriberが必要
use std::collections::HashMap;

use tracing_subscriber::prelude::*;

fn main() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .init();

    let map = HashMap::from([(1, "hoge"), (2, "fuga")]);
    // 「mesasge map={2 => fuga, 1 => hoge}」のように表示される
    tracing::info!(
        map = %std::fmt::from_fn(|f| {
            f.write_str("{")?;
            for (i, (k, v)) in map.iter().enumerate() {
                if i > 0 {
                    f.write_str(", ")?;
                }
                write!(f, "{k} => {v}")?;
            }
            f.write_str("}")?;
            Ok(())
        }),
        "mesasge",
    );
}

// ↑と同じことをやるために型定義をする場合
struct MyDisplay<'a, K, V>(&'a HashMap<K, V>);

impl<'a, K: std::fmt::Display, V: std::fmt::Display> std::fmt::Display for MyDisplay<'a, K, V> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("{")?;
        for (i, (k, v)) in map.iter().enumerate() {
            if i > 0 {
                f.write_str(", ")?;
            }
            write!(f, "{k} => {v}")?;
        }
        f.write_str("}")?;
        Ok(())
    }
}

スライスを配列化できるようになった

これまで、スライスから配列への変換にはTryFromTryIntoが使えました。 ただこれは汎用的な仕組みであるため型の明示が必要になることが多く、使いづらい場面が多々ありました。

Rust 1.93からは[<[T]>::as_array]が使えるようになり、より特殊化された関数であることで書きやすくなりました。 これは定数ジェネリクスの引数として要素数を受け取るもので、slice.as_array::<4>()のように使用します。

また可変スライス版の[<[T]>as_mut_array]も使えます。

[<[T]>::as_array]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array [<[T]>::as_mut_array]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array

// Rust 1.92まで
fn read_f32_try_into(slice: &[u8]) -> Option<f32> {
    let (head, _) = slice.split_at_checked(size_of::<f32>())?;
    // <&_>までは省略できるものの、head.try_into()と書けないなど制約が強かった
    let arr = <&[u8; 4]>::try_from(head).ok()?;
    let v = f32::from_le_bytes(*arr);
    Some(v)
}

// Rust 1.93から
fn read_f32_as_array(slice: &[u8]) -> Option<f32> {
    let (head, _) = slice.split_at_checked(size_of::<f32>())?;
    // 配列であることが明示できる記述が省略できる。要素数も推論できる
    let arr = head.as_array()?;
    let v = f32::from_le_bytes(*arr);
    Some(v)
}

安定化されたAPIのドキュメント

安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。

<[MaybeUninit<T>]>::assume_init_drop

原典

impl<T> [MaybeUninit<T>] {
    #[stable(feature = "maybe_uninit_slice", since = "1.93.0")]
    #[inline(always)]
    #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")]
    pub const unsafe fn assume_init_drop(&mut self)
    where
        T: [const] Destruct,
    { /* 実装は省略 */ }
}

包含する値をその場でドロップする。

安全性

呼び出し元は、スライス内のすべてのMaybeUninit<T>が本当に初期化済みであることを保証しなければならない。 内容がまだ完全に初期化されていない状態でこれを呼び出すと未定義動作になる。

さらに型Tの追加の不変条件について、T(またはそのメンバ)のDrop実装がこれに依存している場合があることから、 これはすべて満たされていなければならない。 たとえば、Vec<T>に無効だが非nullのアドレスを設定すると初期化済みとみなされる(これは現状の実装であり安定した保証はない)。 なぜなら、コンパイラが知っている要件はデータポインタが非nullであることのみだからである。 しかし、そのようなVec<T>を破棄すると未定義動作になる。

<[MaybeUninit<T>]>::assume_init_ref

原典

impl<T> [MaybeUninit<T>] {
    #[stable(feature = "maybe_uninit_slice", since = "1.93.0")]
    #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")]
    #[inline(always)]
    pub const unsafe fn assume_init_ref(&self) -> &[T]
    { /* 実装は省略 */ }
}

包含する値への共有参照を取得する。

安全性

内容がまだ完全に初期化されていない状態でこれを呼び出すと未定義動作になる。 呼び出し元は、スライス内のすべてのMaybeUninit<T>が本当に初期化済みであることを保証しなければならない。

<[MaybeUninit<T>]>::assume_init_mut

原典

impl<T> [MaybeUninit<T>] {
    #[stable(feature = "maybe_uninit_slice", since = "1.93.0")]
    #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")]
    #[inline(always)]
    pub const unsafe fn assume_init_mut(&mut self) -> &mut [T]
    { /* 実装は省略 */ }
}

包含する値への(唯一の)可変参照を取得する。

安全性

内容がまだ完全に初期化されていない状態でこれを呼び出すと未定義動作になる。 呼び出し元は、スライス内のすべてのMaybeUninit<T>が本当に初期化済みであることを保証しなければならない。 例えば.assume_init_mutMaybeUninitのスライスを初期化するために使うことは出来ない。

<[MaybeUninit<T>]>::write_copy_of_slice

原典

impl<T> [MaybeUninit<T>] {
    #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")]
    #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "1.93.0")]
    pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T]
    where
        T: Copy,
    { /* 実装は省略 */ }
}

srcの要素をselfにコピーし、初期化済みのselfへの可変参照を返す。

TCopyでない場合はwrite_clone_of_sliceを使うこと。

これはslice::copy_from_sliceと似ている。

パニック

2つのスライスの長さが異なる場合、この関数は失敗する。

サンプル

use std::mem::MaybeUninit;

let mut dst = [MaybeUninit::uninit(); 32];
let src = [0; 32];

let init = dst.write_copy_of_slice(&src);

assert_eq!(init, src);
let mut vec = Vec::with_capacity(32);
let src = [0; 16];

vec.spare_capacity_mut()[..src.len()].write_copy_of_slice(&src);

// SAFETY: lenの全要素を予備領域にコピーしたので、vecの先頭src.len()要素が有効になった
unsafe {
    vec.set_len(src.len());
}

assert_eq!(vec, src);

<[MaybeUninit<T>]>::write_clone_of_slice

原典

impl<T> [MaybeUninit<T>] {
    #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")]
    pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T]
    where
        T: Clone,
    { /* 実装は省略 */ }
}

srcの要素をselfに複製し、初期化済みのselfへの可変参照を返す。 すでに初期化済みの要素は破棄されない。

TCopyを実装している場合はwrite_copy_of_sliceを使うこと。

これは既存の要素を破棄しない点を除き、slice::clone_from_sliceと似ている。

パニック

2つのスライスの長さが異なる場合や、Cloneの実装が失敗した場合、この関数は失敗する。

失敗が発生した場合、すでに複製された要素は破棄される。

サンプル

use std::mem::MaybeUninit;

let mut dst = [const { MaybeUninit::uninit() }; 5];
let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string());

let init = dst.write_clone_of_slice(&src);

assert_eq!(init, src);
use std::mem::MaybeUninit;

let mut dst = [const { MaybeUninit::uninit() }; 5];
let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string());

let init = dst.write_clone_of_slice(&src);

assert_eq!(init, src);

// Miri用にリークを防止
unsafe { std::ptr::drop_in_place(init); }
let mut vec = Vec::with_capacity(32);
let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string());

vec.spare_capacity_mut()[..src.len()].write_clone_of_slice(&src);

// SAFETY: lenの全要素を予備領域に複製したので、vecの先頭src.len()要素が有効になった
unsafe {
    vec.set_len(src.len());
}

assert_eq!(vec, src);

String::into_raw_parts

原典

impl String {
    #[must_use = "losing the pointer will leak memory"]
    #[stable(feature = "vec_into_raw_parts", since = "1.93.0")]
    pub fn into_raw_parts(self) -> (*mut u8, usize, usize)
    { /* 実装は省略 */ }
}

Stringを生の構成要素(ポインタ、長さ、容量)に分解する。

内部データへの生ポインタ、文字列の長さ(バイト数)、データの割り当て容量(バイト数)を返す。 これらはfrom_raw_parts関数への引数と同じ順序である。

この関数を呼び出した後は、呼び出し元が以前Stringが管理していたメモリの管理責任を持つ。 これを管理する唯一の方法は、生ポインタ・長さ・容量をfrom_raw_parts関数でStringに戻し、 デストラクタによる後始末を可能にすることである。

サンプル

let s = String::from("こんにちは");

let (ptr, len, cap) = s.into_raw_parts();

let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) };
assert_eq!(rebuilt, "こんにちは");

Vec::into_raw_parts

原典

impl<T> Vec<T> {
    #[must_use = "losing the pointer will leak memory"]
    #[stable(feature = "vec_into_raw_parts", since = "1.93.0")]
    pub fn into_raw_parts(self) -> (*mut T, usize, usize)
    { /* 実装は省略 */ }
}

Vec<T>を生の構成要素(ポインタ、長さ、容量)に分解する。

内部データへの生ポインタ、ベクタの長さ(要素数)、データの割り当て容量(要素数)を返す。 これらはfrom_raw_parts関数への引数と同じ順序である。

この関数を呼び出した後は、呼び出し元が以前Vecが管理していたメモリの管理責任を持つ。 多くの場合、生ポインタ・長さ・容量をfrom_raw_parts関数でVecに戻すことで管理する。 より一般的には、Tが非ゼロサイズで容量も非ゼロの場合、 Layout::array::<T>(capacity)のレイアウトでdeallocを呼び出す方法もある。 Tがゼロサイズまたは容量がゼロなら何もする必要はない。

サンプル

let v: Vec<i32> = vec![-1, 0, 1];

let (ptr, len, cap) = v.into_raw_parts();

let rebuilt = unsafe {
    // ここで構成要素を変更することもできる
    // 例えば生ポインタを互換性のある型に変換することも可能
    let ptr = ptr as *mut u32;

    Vec::from_raw_parts(ptr, len, cap)
};
assert_eq!(rebuilt, [4294967295, 0, 1]);

<iN>::unchecked_neg

原典

impl isize {
    #[stable(feature = "unchecked_neg", since = "1.93.0")]
    #[rustc_const_stable(feature = "unchecked_neg", since = "1.93.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline(always)]
    #[track_caller]
    pub const unsafe fn unchecked_neg(self) -> Self
    { /* 実装は省略 */ }
}

検査なしの符号反転。オーバーフローが発生しないことを前提に-selfを計算する。

安全性

self == isize::MINの場合、すなわちchecked_negNoneを返す場合に未定義動作を引き起こす。

<iN>::unchecked_shl

原典

impl isize {
    #[stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline(always)]
    #[track_caller]
    pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self
    { /* 実装は省略 */ }
}

検査なしの左シフト。rhsが自身のビット数未満であることを前提にself << rhsを計算する。

安全性

rhsが自身のビット数以上の場合、すなわちchecked_shlNoneを返す場合に未定義動作を引き起こす。

<iN>::unchecked_shr

原典

impl isize {
    #[stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline(always)]
    #[track_caller]
    pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self
    { /* 実装は省略 */ }
}

検査なしの右シフト。rhsが自身のビット数未満であることを前提にself >> rhsを計算する。

安全性

rhsが自身のビット数以上の場合、すなわちchecked_shrNoneを返す場合に未定義動作を引き起こす。

<uN>::unchecked_shl

原典

impl usize {
    #[stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline(always)]
    #[track_caller]
    pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self
    { /* 実装は省略 */ }
}

検査なしの左シフト。rhsが自身のビット数未満であることを前提にself << rhsを計算する。

安全性

rhsが自身のビット数以上の場合、すなわちchecked_shlNoneを返す場合に未定義動作を引き起こす。

<uN>::unchecked_shr

原典

impl usize {
    #[stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline(always)]
    #[track_caller]
    pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self
    { /* 実装は省略 */ }
}

検査なしの右シフト。rhsが自身のビット数未満であることを前提にself >> rhsを計算する。

安全性

rhsが自身のビット数以上の場合、すなわちchecked_shrNoneを返す場合に未定義動作を引き起こす。

<[T]>::as_array

原典

impl<T> [T] {
    #[stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[inline]
    #[must_use]
    pub const fn as_array<const N: usize>(&self) -> Option<&[T; N]>
    { /* 実装は省略 */ }
}

配列の参照を取得する。

Nselfの長さと一致しない場合、このメソッドはNoneを返す。

<[T]>::as_mut_array

原典

impl<T> [T] {
    #[stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[inline]
    #[must_use]
    pub const fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]>
    { /* 実装は省略 */ }
}

配列の可変参照を取得する。

Nselfの長さと一致しない場合、このメソッドはNoneを返す。

<*const [T]>::as_array

原典

impl<T> *const [T] {
    #[stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[inline]
    #[must_use]
    pub const fn as_array<const N: usize>(self) -> Option<*const [T; N]>
    { /* 実装は省略 */ }
}

配列への生ポインタを取得する。

Nselfの長さと一致しない場合、このメソッドはNoneを返す。

<*mut [T]>::as_mut_array

原典

impl<T> *mut [T] {
    #[stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")]
    #[inline]
    #[must_use]
    pub const fn as_mut_array<const N: usize>(self) -> Option<*mut [T; N]>
    { /* 実装は省略 */ }
}

配列への生の可変ポインタを取得する。

Nselfの長さと一致しない場合、このメソッドはNoneを返す。

VecDeque::pop_front_if

原典

impl<T, A: Allocator> VecDeque<T, A> {
    #[stable(feature = "vec_deque_pop_if", since = "1.93.0")]
    pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T>
    { /* 実装は省略 */ }
}

述語関数がtrueを返した場合、両端キューの先頭要素を取り除いて返す。 述語関数がfalseを返すか、(述語関数は呼ばれないが)キューが空の場合はNoneを返す。

サンプル

use std::collections::VecDeque;

let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into();
let pred = |x: &mut i32| *x % 2 == 0;

assert_eq!(deque.pop_front_if(pred), Some(0));
assert_eq!(deque, [1, 2, 3, 4]);
assert_eq!(deque.pop_front_if(pred), None);

VecDeque::pop_back_if

原典

impl<T, A: Allocator> VecDeque<T, A> {
    #[stable(feature = "vec_deque_pop_if", since = "1.93.0")]
    pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T>
    { /* 実装は省略 */ }
}

述語関数がtrueを返した場合、両端キューの末尾要素を取り除いて返す。 述語関数がfalseを返すか、(述語関数は呼ばれないが)キューが空の場合はNoneを返す。

サンプル

use std::collections::VecDeque;

let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into();
let pred = |x: &mut i32| *x % 2 == 0;

assert_eq!(deque.pop_back_if(pred), Some(4));
assert_eq!(deque, [0, 1, 2, 3]);
assert_eq!(deque.pop_back_if(pred), None);

Duration::from_nanos_u128

原典

impl Duration {
    #[stable(feature = "duration_from_nanos_u128", since = "1.93.0")]
    #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "1.93.0")]
    #[must_use]
    #[inline]
    #[track_caller]
    #[rustc_allow_const_fn_unstable(const_trait_impl, const_convert)] // for `u64::try_from`
    pub const fn from_nanos_u128(nanos: u128) -> Duration
    { /* 実装は省略 */ }
}

指定されたナノ秒数から新しいDurationを生成する。

パニック

与えられたナノ秒数がDuration::MAXより大きい場合はパニックする。

サンプル

use std::time::Duration;

let nanos = 10_u128.pow(24) + 321;
let duration = Duration::from_nanos_u128(nanos);

assert_eq!(10_u64.pow(15), duration.as_secs());
assert_eq!(321, duration.subsec_nanos());

char::MAX_LEN_UTF8

原典

impl char {
    #[stable(feature = "char_max_len_assoc", since = "1.93.0")]
    pub const MAX_LEN_UTF8: usize = 4;
}

charUTF-8符号化する際に必要な最大バイト数。

char::MAX_LEN_UTF16

原典

impl char {
    #[stable(feature = "char_max_len_assoc", since = "1.93.0")]
    pub const MAX_LEN_UTF16: usize = 2;
}

charUTF-16符号化する際に必要な最大2バイト単位数。

std::fmt::from_fn

原典

#[stable(feature = "fmt_from_fn", since = "1.93.0")]
#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"]
pub fn from_fn<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(f: F) -> FromFn<F>
{ /* 実装は省略 */ }

渡されたクロージャfmt::Debugfmt::Displayの実装を委ねる型を生成する。

サンプル

use std::fmt;

let value = 'a';
assert_eq!(format!("{}", value), "a");
assert_eq!(format!("{:?}", value), "'a'");

let wrapped = fmt::from_fn(|f| write!(f, "{value:?}"));
assert_eq!(format!("{}", wrapped), "'a'");
assert_eq!(format!("{:?}", wrapped), "'a'");

std::fmt::FromFn

原典

#[stable(feature = "fmt_from_fn", since = "1.93.0")]
pub struct FromFn<F>(F);

渡されたクロージャを使ってfmt::Debugfmt::Displayを実装する。

from_fnで生成される。

変更点リスト

言語

コンパイラ

プラットフォーム対応

Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照

ライブラリ

安定化されたAPI

Cargo

Rustdoc

互換性メモ

関連リンク

さいごに

次のリリースのRust 1.94は3/6(金)にリリースされる予定です。 Rust 1.94ではスライスからN個ごとに配列を取り出せるようになったり、LazyLockから初期化済みの値を取り出せるようになったりするようです。

ライセンス表記

  • この記事はApache 2/MITのデュアルライセンスで公開されている公式リリースノート及びドキュメントから翻訳・追記をしています
  • 冒頭の画像中にはRust公式サイトで配布されているロゴを使用しており、 このロゴはRust財団によってCC-BYの下で配布されています
  • 冒頭の画像はいらすとやさんの画像を使っています。いつもありがとうございます



以上の内容はhttps://aznhe21.hatenablog.com/entry/2026/01/23/rust-1.93より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14