本日1/23(金)にリリースされたRust 1.93の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
この記事は原文の理解や和訳のために一部生成AIを使用していますが、すべて筆者の考えに基づく文章で構成しており、 漫然と生成AIを使用しているものではありません。
- ピックアップ
- 安定化されたAPIのドキュメント
- <[MaybeUninit<T>]>::assume_init_drop
- <[MaybeUninit<T>]>::assume_init_ref
- <[MaybeUninit<T>]>::assume_init_mut
- <[MaybeUninit<T>]>::write_copy_of_slice
- <[MaybeUninit<T>]>::write_clone_of_slice
- String::into_raw_parts
- Vec::into_raw_parts
- <iN>::unchecked_neg
- <iN>::unchecked_shl
- <iN>::unchecked_shr
- <uN>::unchecked_shl
- <uN>::unchecked_shr
- <[T]>::as_array
- <[T]>::as_mut_array
- <*const [T]>::as_array
- <*mut [T]>::as_mut_array
- VecDeque::pop_front_if
- VecDeque::pop_back_if
- Duration::from_nanos_u128
- char::MAX_LEN_UTF8
- char::MAX_LEN_UTF16
- std::fmt::from_fn
- std::fmt::FromFn
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
内部可変性のある型を定数で扱うと警告が出るようになった
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が使えるようになり、クロージャを渡すことでDisplayやDebugを簡易に実装することができるようになりました。
例えばtracingクレートではtracing::info!(value = %val, "message)のようにすることでvalを文字列化したものをイベントに紐付けることが出来ます。
ただしvalはDisplayを実装している必要がありますが、ここで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(()) } }
スライスを配列化できるようになった
これまで、スライスから配列への変換にはTryFrom・TryIntoが使えました。
ただこれは汎用的な仕組みであるため型の明示が必要になることが多く、使いづらい場面が多々ありました。
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_mutはMaybeUninitのスライスを初期化するために使うことは出来ない。
<[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への可変参照を返す。
TがCopyでない場合は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への可変参照を返す。
すでに初期化済みの要素は破棄されない。
TがCopyを実装している場合は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_negがNoneを返す場合に未定義動作を引き起こす。
<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_shlがNoneを返す場合に未定義動作を引き起こす。
<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_shrがNoneを返す場合に未定義動作を引き起こす。
<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_shlがNoneを返す場合に未定義動作を引き起こす。
<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_shrがNoneを返す場合に未定義動作を引き起こす。
<[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]> { /* 実装は省略 */ } }
配列の参照を取得する。
Nがselfの長さと一致しない場合、このメソッドは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]> { /* 実装は省略 */ } }
配列の可変参照を取得する。
Nがselfの長さと一致しない場合、このメソッドは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]> { /* 実装は省略 */ } }
配列への生ポインタを取得する。
Nがselfの長さと一致しない場合、このメソッドは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]> { /* 実装は省略 */ } }
配列への生の可変ポインタを取得する。
Nがselfの長さと一致しない場合、このメソッドは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; }
char::MAX_LEN_UTF16
impl char { #[stable(feature = "char_max_len_assoc", since = "1.93.0")] pub const MAX_LEN_UTF16: usize = 2; }
charをUTF-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::Debugとfmt::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::Debugとfmt::Displayを実装する。
from_fnで生成される。
変更点リスト
言語
- s390xの
vector関連ターゲット機能とis_s390x_feature_detected!マクロを安定化 systemABIでC風可変長関数の宣言を安定化- 一部のキーワードを
cfg条件式で使うとエラーを出すようになった asm_cfgを安定化- 定数評価中でのポインタのバイト単位コピーをサポート
- LUB強制変換が関数アイテム型や異なる安全性を持つ関数を正しく扱うようになった
constアイテムがstaticへの可変参照を含むことを許可(非常に危険だが常に未定義動作とは限らない)- 内部可変な
constアイテムを変更する呼び出しに警告を出す、既定警告のリントconst_item_interior_mutationsを追加 - 既定警告の
function_casts_as_integerリントを追加
コンパイラ
-Cjump-tables=boolを安定化(以前は-Zno-jump-tablesとして使われていた)
プラットフォーム対応
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
- 標準ライブラリ内部で
Copyトレイトの特殊化を行わないようになった。ライフタイム依存のCopy実装がある場合に不良動作となるため。これにより一部の標準ライブラリAPIでビット単位コピーの代わりにClone::cloneが呼ばれるようになり、性能が低下する場合がある - グローバルアロケータがスレッドローカルストレージや
std::thread::current()を使えるようになった BTree::appendで既存のキーに対して値を追加する際、既存のキーを更新しないようになったvec::IntoIter<T>: UnwindSafeにT: RefUnwindSafeが不要になった
安定化されたAPI
<[MaybeUninit<T>]>::assume_init_drop<[MaybeUninit<T>]>::assume_init_ref<[MaybeUninit<T>]>::assume_init_mut<[MaybeUninit<T>]>::write_copy_of_slice<[MaybeUninit<T>]>::write_clone_of_sliceString::into_raw_partsVec::into_raw_parts<iN>::unchecked_neg<iN>::unchecked_shl<iN>::unchecked_shr<uN>::unchecked_shl<uN>::unchecked_shr<[T]>::as_array<[T]>::as_mut_array<*const [T]>::as_array<*mut [T]>::as_mut_arrayVecDeque::pop_front_ifVecDeque::pop_back_ifDuration::from_nanos_u128char::MAX_LEN_UTF8char::MAX_LEN_UTF16std::fmt::from_fnstd::fmt::FromFn
Cargo
- プロファイルに応じてビルドスクリプトでCARGO_CFG_DEBUG_ASSERTIONSを有効化
cargo treeの--formatで長い形式の変数に対応cargo cleanに--workspaceオプションを追加
Rustdoc
#![doc(document_private_items)]を削除- 検索フィルタ「macros」に属性マクロと自動導出マクロを含める
- 検索フィルタ「import」に外部クレートを含める
- クレート階層における文書属性の利用を検証。
html_favicon_url・html_logo_url・html_playground_url・issue_tracker_base_url・html_no_sourceなどの値が無かったり、不正だったり、型が違ったりした場合、既定拒否のリントrustdoc::invalid_doc_attributesが出力される
互換性メモ
- 組み込み属性名前空間に
pin_v2を導入 - 同梱されているmuslを1.2.5に更新
- Emscriptenにおいて、
panic=unwind時の巻き戻しABIをJS例外処理ABIからwasm例外処理ABIに変更。C/C++オブジェクトファイルとRustオブジェクトをリンクする場合は-fwasm-exceptionsをリンカに渡す必要がある。Nightlyでは-Zwasm-emscripten-eh=false -Zbuild-stdにより従来通り動作させることも可能だが、将来的に削除される予定 - テストを定義する
#[test]属性が(トレイトメソッドや型など)無意味な場所に付いている場合は無視されていた。これらの#[test]属性は無視されなくなり、エラーになるようになった。これによりrustdoc生成時にもエラーになる場合がある。構造体のtest属性でエラー - Cargoがより多くの状況で
CARGO_CFG_DEBUG_ASSERTIONS環境変数を設定するようになった。これによりstatic-initのバージョン1.0.1~1.0.3に依存するクレートは「failed to resolve: use of unresolved module or unlinked crateparking_lot」でコンパイルに失敗する。詳細はリンク先のissueを参照 offset_of!マクロに書かれた型が正しい形式か検証するようになったcargo publishでbuild.build-dir設定が未指定の場合、.crateファイルを最終成果物として出力しなくなった- リント
deref_nullptrリントを既定警告から既定エラーに昇格 externブロック外でパターンなしの...関数引数向けに将来的互換性警告を追加repr(C)な列挙型の判別値がc_intやc_uintに収まらない場合向けに将来的互換性警告を導入repr(transparent)の一部としてrepr(C)型を無視することに対する将来的互換性警告を導入
関連リンク
さいごに
次のリリースのRust 1.94は3/6(金)にリリースされる予定です。
Rust 1.94ではスライスからN個ごとに配列を取り出せるようになったり、LazyLockから初期化済みの値を取り出せるようになったりするようです。