本日6/27(金)にリリースされたRust 1.88の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
この記事は原文の理解や和訳のために一部生成AIを使用していますが、すべて筆者の考えに基づく文章で構成しており、 漫然と生成AIを使用しているものではありません。
- ピックアップ
- 安定化されたAPIのドキュメント
- Cell::update
- HashMap::extract_if
- HashSet::extract_if
- hint::select_unpredictable
- proc_macro::Span::line
- proc_macro::Span::column
- proc_macro::Span::start
- proc_macro::Span::end
- proc_macro::Span::file
- proc_macro::Span::local_file
- <[T]>::as_chunks
- <[T]>::as_chunks_mut
- <[T]>::as_chunks_unchecked
- <[T]>::as_chunks_unchecked_mut
- <[T]>::as_rchunks
- <[T]>::as_rchunks_mut
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
ifの条件式でletを繋げられるようになった
if式でletを使う場合はそれ以外の条件を書けず、無駄にifを入れ子にさせられて残念な思いをした方も多いのではないのでしょうか。
Rust 1.88ではif内にletや条件式を複数繋げることができるようになり、書きやすさが格段に上がりました。
ただしこれを使うにはRust 2024版が必要です。 というか2024版のライフタイム周りの仕様変更はこの機能のためでした。
use std::io::Read; fn main() { let x = Some("hoge".to_string()); // Option::is_some_andを使うにはas_refが邪魔だった if x.as_ref().is_some_and(|x| x.len() % 2 == 0) { println!("xの長さは偶数"); } // 中身を使うにはif-letを使うが二重ifになるのがモヤる if let Some(x) = &x { if x.len() % 2 == 0 { println!("xの長さは偶数:{x}"); } } // Rust 1.88(2024版)からはこんなにスッキリ if let Some(x) = &x && x.len() % 2 == 0 { println!("xの長さは偶数:{x}"); } // 何重に重ねても良いし、常にtrueとなる式を書いても良い // が読みにくいので普通に書いた方が良い // (この例なら普通にstd::fs::readを使おうね) if let Ok(mut f) = std::fs::File::open("fuga") && let Ok(metadata) = f.metadata() && let mut x = String::with_capacity(metadata.len() as usize) && let Ok(_) = f.read_to_string(&mut x) && let Ok(x) = x.trim().parse::<usize>() && x % 2 == 0 { println!("ファイルの中身は偶数:{x}"); } }
ちなみにこの機能はRust 1.64(2022年10月22日リリース)で 使えるようになる寸前のところまで行ったのですが、この 仕様変更なしには導入できないことが判明し差し戻されたという経緯がありました。 それから2年半越しにようやく正式導入されることとなり、待ち焦がれていたという人も多いことでしょう。
スライスを配列へ分割できるようになった
<[T]>::as_chunks及びその亜種が追加され、スライスからN要素の配列へのスライス(と余り)へ分割できるようになりました。
これまでも<[T]>::chunksで似たようなことは出来ましたが、要素数が固定であってもスライスでしか得られず、
無駄にtry_into()する必要があったりと美しくはありませんでした。
fn chunks(slice: &[u8]) { for chunk in slice.chunks(2) { // 型注釈が必要だったり余りを処理する必要があったりとノイズが多い let Ok(&[hi, lo]): Result<&[_; 2], _> = chunk.try_into() else { break; }; let n = u16::from_ne_bytes([hi, lo]); println!("{n}"); } } fn as_chunks(slice: &[u8]) { // 素直に書けてスッキリ let (chunks, _remainder) = slice.as_chunks::<2>(); // 余りがないことを検査したい場合はlet-else // let (chunks, []) = slice.as_chunks::<2>() else { todo!() }; for &[hi, lo] in chunks { let n = u16::from_ne_bytes([hi, lo]); println!("{n}"); } }
as_chunksは余りを末尾で求める不変借用版ですが、次の亜種もあります。
as_chunks_mut:余りを末尾で求める可変借用版as_chunks_unchecked:余りがない前提の不変借用版as_chunks_unchecked_mut:余りがない前提の可変借用版as_rchunks:余りを先頭で求める不変借用版as_rchunks_mut:余りを先頭で求める可変借用版
(unchecked版は余りがない前提なのでas_rchunks_uncheckedはない)
コンパイルを常に無効化(有効化)できるようになった
#[cfg(true)]や#[cfg(false)]のようにcfgの中に真偽値を入れられるようになり、
式やブロックを常に有効化・無効化できるようになりました。基本的には#[cfg(false)]の方を使うような気がします。
デバッグ時には特定のブロックをコンパイルさせたくないということはままありますが、 コメントでは範囲すべてを編集する必要があるなど少し面倒でした。
これまでも未定義の条件を使い#[cfg(FALSE)]のように書くこともできましたが、
これではunexpected `cfg` condition name: `FALSE`という警告が出るため少し不便でした。
fn super_convenient_func() { // 依存から一旦tokioを外したいので無効化しとく #[cfg(false)] { tokio::spawn(async { println!("mettya benri!"); }); } }
cargo実行時に自動でゴミが掃除されるようになった
~/.cargo以下には全プロジェクトの依存クレートがダウンロード・キャッシュされますが、
長い間開発していると古いクレートがどんどん貯まっていきます。
このキャッシュは使われていないものだけでも数GBになることもあり、邪魔と思ったことがあるかもしれません。
Rust 1.88以降のCargoでは、(一部cargoコマンド実行時に)ダウンロードから3ヶ月経過後に自動で掃除してくれるようになります。
既定では1日に1回発動しますが、この頻度は設定値gc.auto.frequencyで変更することもできます。
なお、これを手動で実行できるコマンドは設計が未確定のためまだ使えません。
またtargetディレクトリのお掃除機能は別途検討中の様です。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
Cell::update
impl<T: Copy> Cell<T> { #[inline] #[stable(feature = "cell_update", since = "1.88.0")] pub fn update(&self, f: impl FnOnce(T) -> T) { /* 実装は省略 */ } }
格納されている値を関数により更新する。
サンプル
use std::cell::Cell; let c = Cell::new(5); c.update(|x| x + 1); assert_eq!(c.get(), 6);
HashMap::extract_if
impl<K, V, S> HashMap<K, V, S> { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, { /* 実装は省略 */ } }
要素(キーと値の組)を削除すべきかについて、クロージャを使って判断するイテレータを生成する。
クロージャがtrueを返すと要素は連想配列から削除され、イテレータから返される。
クロージャがfalseを返すかパニックした場合、その要素は連想配列に残ってイテレータからは返されない。
このイテレータでは各要素を削除するかどうかにかかわらず、クロージャ内で値を変更できる。
そのままドロップしたり途中で繰り返しをやめたりした時など、戻り値のExtractIfを処理しきらなかった場合
残った要素は保持される。
戻り値のイテレータが不要な場合は述語関数を反転させてretainを使うと良い。
サンプル
元の連想配列を再利用し、キーを偶数と奇数に分割する。
use std::collections::HashMap; let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect(); let extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect(); let mut evens = extracted.keys().copied().collect::<Vec<_>>(); let mut odds = map.keys().copied().collect::<Vec<_>>(); evens.sort(); odds.sort(); assert_eq!(evens, vec![0, 2, 4, 6]); assert_eq!(odds, vec![1, 3, 5, 7]);
HashSet::extract_if
impl<T, S> HashSet<T, S> { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F> where F: FnMut(&T) -> bool, { /* 実装は省略 */ } }
要素を削除すべきかについて、クロージャを使って判断するイテレータを生成する。
クロージャがtrueを返すと要素は集合から削除され、イテレータから返される。
クロージャがfalseを返すかパニックした場合、その要素は集合に残ってイテレータからは返されない。
そのままドロップしたり途中で繰り返しをやめたりした時など、戻り値のExtractIfを処理しきらなかった場合
残った要素は保持される。
戻り値のイテレータが不要な場合は述語関数を反転させてretainを使うと良い。
サンプル
元の集合を再利用し、偶数と奇数に分割する。
use std::collections::HashSet; let mut set: HashSet<i32> = (0..8).collect(); let extracted: HashSet<i32> = set.extract_if(|v| v % 2 == 0).collect(); let mut evens = extracted.into_iter().collect::<Vec<_>>(); let mut odds = set.into_iter().collect::<Vec<_>>(); evens.sort(); odds.sort(); assert_eq!(evens, vec![0, 2, 4, 6]); assert_eq!(odds, vec![1, 3, 5, 7]);
hint::select_unpredictable
#[inline(always)] #[stable(feature = "select_unpredictable", since = "1.88.0")] pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T { /* 実装は省略 */ }
conditionの値に応じてtrue_valまたはfalse_valを返すが、
conditionがCPUの分岐予測器によって正しく予測されにくいことをコンパイラに伝える。
この関数は、機能的には次のコードと同じである。
fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T { if b { true_val } else { false_val } }
ただし、生成される機械語が異なる場合がある。特に、条件付き移動命令や選択命令(x86のcmovやARMのcselなど)がある
プラットフォームでは最適化によって分岐を避ける命令が使われることがあり、
(二分探索の実装など)分岐予測が難しい場合に性能向上が期待できる。
ただし、このような引き下げ(lowering)が必ず行われるとは限らず(どのプラットフォームでも保証されない)、
暗号用途などで定数時間動作を書きたい場合には頼るべきではない。
また、conditionが予測しやすい場合にはこの引き下げ(lowering)により逆に性能が悪化することもあるため、
この関数が有用かどうかは実際に計測して判断することを勧める。
サンプル
値を2つの入れ物へ均等に振り分ける
use std::hash::BuildHasher; use std::hint; fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) { let hash = hasher.hash_one(&v); let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two); bucket.push(v); }
use std::hash::BuildHasher;
use std::hint;
fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
let hash = hasher.hash_one(&v);
let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two);
bucket.push(v);
}
let hasher = std::collections::hash_map::RandomState::new();
let mut bucket_one = Vec::new();
let mut bucket_two = Vec::new();
append(&hasher, 42, &mut bucket_one, &mut bucket_two);
assert_eq!(bucket_one.len() + bucket_two.len(), 1);
proc_macro::Span::line
impl Span { #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { /* 実装は省略 */ } }
Spanが開始する位置の、ソースファイルにおける1始まりの行番号。
Spanが終了する位置の行番号を取得したい場合はspan.end().line()を使うこと。
proc_macro::Span::column
impl Span { #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { /* 実装は省略 */ } }
Spanが開始する位置の、ソースファイルにおける1始まりの列番号。
Spanが終了する位置の列番号を取得したい場合はspan.end().column()を使うこと。
proc_macro::Span::start
impl Span { #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { /* 実装は省略 */ } }
このSpanの直前を指す空のSpanを生成する。
proc_macro::Span::end
impl Span { #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { /* 実装は省略 */ } }
このSpanの直後を指す空のSpanを作成する。
proc_macro::Span::file
impl Span { #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn file(&self) -> String { /* 実装は省略 */ } }
表示用の、このSpanが発生したソースファイルのパス。
これは有効なファイルシステム上のパスとは限らない。
このパスは再マッピングされていたり(例: "/src/lib.rs")、人工的なパスだったり(例: "<command line>")する可能性がある。
proc_macro::Span::local_file
impl Span { #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn local_file(&self) -> Option<PathBuf> { /* 実装は省略 */ } }
ローカルファイルシステム上における、このSpanが発生したソースファイルのパス。
これはディスク上の実際のパスであり、パスの再マッピングの影響を受けない。
このパスはマクロの出力に埋め込むべきではない。代わりに file() を使用すること。
<[T]>::as_chunks
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] pub const fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) { /* 実装は省略 */ } }
スライスをN要素配列のスライスと、長さがN未満の余りスライスに分割する。
余りは割り算の余りとしての意味がある。let (chunks, remainder) = slice.as_chunks()とした場合、次が成り立つ。
chunks.len()はslice.len() / Nに等しいremainder.len()はslice.len() % Nに等しいslice.len()はchunks.len() * N + remainder.len()に等しい
配列塊はas_flattenedにより再びTのスライスに戻すことができる。
パニック
Nがゼロの場合はパニックする。
この検査は、実行時値ではなく定数の総称引数に対して行われるため、 特定の単一化(monomorphization)では常にパニックし、それ以外では決してパニックしない。
サンプル
let slice = ['l', 'o', 'r', 'e', 'm']; let (chunks, remainder) = slice.as_chunks(); assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]); assert_eq!(remainder, &['m']);
スライスがちょうど割り切れることを期待する場合は、let-else構文と空スライスパターンを組み合わせると良い。
let slice = ['R', 'u', 's', 't']; let (chunks, []) = slice.as_chunks::<2>() else { panic!("sliceの長さが偶数ではない didn't have even length") }; assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
<[T]>::as_chunks_mut
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] pub const fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) { /* 実装は省略 */ } }
スライスをN要素配列のスライスと、長さがN未満の余りスライスに分割する。
余りは割り算の余りとしての意味がある。let (chunks, remainder) = slice.as_chunks_mut()とした場合、次が成り立つ。
chunks.len()はslice.len() / Nに等しい
remainder.len()はslice.len() % Nに等しい
slice.len()はchunks.len() * N + remainder.len()に等しい
配列塊はas_flattened_mutにより再びTのスライスに戻すことができる。
パニック
この検査は、実行時値ではなく定数の総称引数に対して行われるため、 特定の単一化(monomorphization)では常にパニックし、それ以外では決してパニックしない。
サンプル
let v = &mut [0, 0, 0, 0, 0]; let mut count = 1; let (chunks, remainder) = v.as_chunks_mut(); remainder[0] = 9; for chunk in chunks { *chunk = [count; 2]; count += 1; } assert_eq!(v, &[1, 1, 2, 2, 9]);
<[T]>::as_chunks_unchecked
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] #[track_caller] pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { /* 実装は省略 */ } }
余りがないものと仮定し、スライスをN要素配列のスライスに分割する。
これはas_flattenedの逆の操作である。
これはunsafeであるため、代わりにas_chunksやas_rchunksの使用を検討すること。
例えばif let (chunks, []) = slice.as_chunks()や
let (chunks, []) = slice.as_chunks() else { unreachable!() };のように書ける。
安全性
この関数を呼び出せるのは次の場合のみである。
- スライスがN要素ごとにちょうど分割できる(つまりself.len() % N == 0)
- N != 0
サンプル
let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!']; let chunks: &[[char; 1]] = // 安全性:1要素ごとの分割なら余りが出ることはない unsafe { slice.as_chunks_unchecked() }; assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]); let chunks: &[[char; 3]] = // 安全性:スライス長(6)は3の倍数 unsafe { slice.as_chunks_unchecked() }; assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]); // これらは不良動作(unsound) // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // スライス長が5の倍数ではない // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // 長さゼロの配列塊は許されない
<[T]>::as_chunks_unchecked_mut
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] #[track_caller] pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { /* 実装は省略 */ } }
余りがないものと仮定し、スライスをN要素配列のスライスに分割する。
これはas_flattened_mutの逆の操作である。
これはunsafeであるため、代わりにas_chunks_mutやas_rchunks_mutの使用を検討すること。
例えばif let (chunks, []) = slice.as_chunks_mut()や
let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };のように書ける。
安全性
この関数を呼び出せるのは次の場合のみである。
- スライスが
N要素ごとにちょうど分割できる(つまりself.len() % N == 0) N != 0
サンプル
let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!']; let chunks: &mut [[char; 1]] = // 安全性:1要素ごとの分割なら余りが出ることはない unsafe { slice.as_chunks_unchecked_mut() }; chunks[0] = ['L']; assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]); let chunks: &mut [[char; 3]] = // 安全性:スライス長(6)は3の倍数 unsafe { slice.as_chunks_unchecked_mut() }; chunks[1] = ['a', 'x', '?']; assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']); // これらは不良動作 // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // スライス長が5の倍数ではない // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // 長さゼロの配列塊は許されない
<[T]>::as_rchunks
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] pub const fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]) { /* 実装は省略 */ } }
スライスを末尾からN要素配列のスライスに分割し、長さがN未満の余りスライスを返す。
余りは割り算の余りとしての意味がある。let (remainder, chunks) = slice.as_rchunks()とした場合、次が成り立つ。
remainder.len()はslice.len() % Nに等しいchunks.len()はslice.len() / Nに等しいslice.len()はchunks.len() * N + remainder.len()に等しい
配列塊はas_flattenedにより再びTのスライスに戻すことができる。
パニック
Nがゼロの場合はパニックする。
この検査は、実行時値ではなく定数の総称引数に対して行われるため、 特定の単一化(monomorphization)では常にパニックし、それ以外では決してパニックしない。
サンプル
let slice = ['l', 'o', 'r', 'e', 'm']; let (remainder, chunks) = slice.as_rchunks(); assert_eq!(remainder, &['l']); assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
<[T]>::as_rchunks_mut
impl<T> [T] { #[stable(feature = "slice_as_chunks", since = "1.88.0")] #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] pub const fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]) { /* 実装は省略 */ } }
スライスを末尾からN要素配列のスライスに分割し、長さがN未満の余りスライスを返す。
余りは割り算の余りとしての意味がある。let (remainder, chunks) = slice.as_rchunks_mut()とした場合、次が成り立つ。
remainder.len()はslice.len() % Nに等しいchunks.len()はslice.len() / Nに等しいslice.len()はchunks.len() * N + remainder.len()に等しい
配列塊はas_flattened_mutにより再びTのスライスに戻すことができる。
パニック
Nがゼロの場合はパニックする。
この検査は、実行時値ではなく定数の総称引数に対して行われるため、 特定の単一化(monomorphization)では常にパニックし、それ以外では決してパニックしない。
サンプル
let v = &mut [0, 0, 0, 0, 0]; let mut count = 1; let (remainder, chunks) = v.as_rchunks_mut(); remainder[0] = 9; for chunk in chunks { *chunk = [count; 2]; count += 1; } assert_eq!(v, &[9, 1, 1, 2, 2]);
変更点リスト
言語
2024版で
#![feature(let_chains)]を安定化。ifやwhileの中で&&を使ってlet文を連結できるようになり、真偽値式と組み合わせて使うことができるようになった。let部分式内のパターンは、論駁の可否を問わない#![feature(naked_functions)]を安定化。 裸の関数を使うことで、関数ごとにアセンブリの生成を完全に制御できるようになり、コンパイラによる前後処理が生成されなくなる#![feature(cfg_boolean_literals)]を安定化。#[cfg(true)]や#[cfg(false)]のように、真偽値リテラルをcfg条件として使えるようになった。#[bench]属性を完全に非推奨化。 Rust 1.77以降で#![feature(custom_test_frameworks)]なしに#[bench]を使うと将来の非互換性リントが既に発生していたが、今後は厳密なエラーとなる。- 生ポインタの逆参照における暗黙的な自動参照に対して既定警告するリント
dangerous_implicit_autorefsを追加。 このリントは次のバージョンで既定エラーに引き上げられる予定である。 - 無効な
ぬるぽをガッするためのヌルポインタの利用を防ぐためのリントinvalid_null_argumentsを追加。 このリントはclippy::invalid_null_ptr_usageから昇格したものである。 - 組み込みの実装や自明なwhere句を持つトレイト実装候補の優先順位を変更
- 総称定数パラメータにおける既定値の型を検査するようにした
コンパイラ
プラットフォーム対応
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
#[should_panic]属性によるテスト失敗時のメッセージからバッククォートを削除- 状態を持つクロージャを渡した場合でも、
[T; N]::from_fnが添字の小さい順に生成することを保証 - libtestの
--nocaptureフラグが非推奨化され、より一貫性のある--no-captureフラグに置き換えられた {float}::NANがquiet NaNであることを保証
安定化されたAPI
Cell::updateimpl Default for *const Timpl Default for *mut THashMap::extract_ifHashSet::extract_ifhint::select_unpredictableproc_macro::Span::lineproc_macro::Span::columnproc_macro::Span::startproc_macro::Span::endproc_macro::Span::fileproc_macro::Span::local_file<[T]>::as_chunks<[T]>::as_chunks_mut<[T]>::as_chunks_unchecked<[T]>::as_chunks_unchecked_mut<[T]>::as_rchunks<[T]>::as_rchunks_mutmod ffi::c_str
以下のAPIが定数文脈で使えるようになった。
NonNull<T>::replace<*mut T>::replacestd::ptr::swap_nonoverlappingCell::{replace, get, get_mut, from_mut, as_slice_of_cells}
Cargo
Rustdoc
ignore-*属性により、ターゲット名に基づいて文書化テストを無視できるようになった- 文書化テストを実行するための(qemuなどの)プログラムとその引数を指定できるCLIオプション
--test-runtoolと--test-runtool-argを安定化
互換性メモ
- 貼り付けられたトークンの内部表現の変更を完了。
これまで一部の特殊な状況でコンパイラが受理していた無効な宣言的マクロは、今後は正しく拒否される。多くの場合、
ttフラグメント指定子を使うことでこれらのマクロを修正できる #[bench]属性を完全に非推奨化。 Rust 1.77以降で#![feature(custom_test_frameworks)]なしに#[bench]を使うと将来の非互換性リントが既に発生していたが、今後は厳密なエラーとなる。- 常に真となる一部パターンに対する借用検査を修正。 これまでは場合によっては借用チェッカーが過剰に寛容であり、本来コンパイルできないはずのプログラムを許可していた
- 最小の外部LLVMをバージョン19に更新した。
- 必要なターゲット機能を有効化せず、非Rust ABIでベクトル型を使うと厳密なエラーとなるようにした。
関連リンク
さいごに
次のリリースのRust 1.89は8/8(金)にリリースされる予定です。
Rust 1.89では配列の要素数を_で推論できるようになったり、format_args!()が変数に入るようになったりするようです。