本日3/22(金)にリリースされたRust 1.77の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
- ピックアップ
- 最近のrust-analyzer
- トレイトのかざしヒントに関連アイテムが表示されるようになった
- 安定化されたAPIのドキュメント
- array::each_ref
- array::each_mut
- float::round_ties_even
- offset_of!
- slice::first_chunk
- slice::first_chunk_mut
- slice::split_first_chunk
- slice::split_first_chunk_mut
- slice::last_chunk
- slice::last_chunk_mut
- slice::split_last_chunk
- slice::split_last_chunk_mut
- slice::chunk_by
- slice::chunk_by_mut
- File::create_new
- Mutex::clear_poision
- RwLock::clear_poision
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
C文字列リテラルが使えるようになった
c"hoge"
のような形で文字列を記述することで、末尾にNULL(\0
)を含む文字列をstd::ffi::CStr
型で得られるようになりました。
これまではCStr::from_bytes_with_nul
を呼び出したりマクロで記述したりする必要があるなど少し面倒でした。
use std::ffi::CStr; macro_rules! cstr { ($lit:literal) => { match CStr::from_bytes_with_nul($lit) { Ok(v) => v, Err(_) => panic!("invalid c-string literal"), } }; } // X、Y、Zどれも同じ意味 const X: &CStr = match CStr::from_bytes_with_nul(b"hoge\0") { Ok(v) => v, Err(_) => panic!("invalid c-string literal"), }; // 宣言型マクロでは\0を付ける必要がある(手続き型マクロなcstrクレートを使えば付ける必要はない) const Y: &CStr = cstr!(b"hoge\0"); // Rust 1.77からは const Z: &CStr = c"hoge"; // リテラル内に\0を含めることはできない // const BAD: &CStr = c"\0"; // ^^ // error: null characters in C string literals are not supported
非同期関数を再帰呼び出しできるようになった
参照(主にはBox
)を経由するという制限があるものの、非同期関数を再帰呼び出しすることができるようになりました。
async fn async_fibonacci(i: u32) -> u32 { if i == 0 || i == 1 { i } else { Box::pin(async_fibonacci(i - 1)).await + Box::pin(async_fibonacci(i - 2)).await } }
スライスの先頭や末尾を配列として切り出せるようになった
slice::{first,last}_chunk{,_mut}
により、スライス&[T]
から先頭や末尾のN
要素を&[T; N]
として切り出せるようになりました。
要素数が足りない場合はNone
が返るためパニックする心配もなく便利です。
また後続(先行)するスライスを得るためのslice::split_{first,last}_chunk{,_mut}
もあります。
/// `slice`の先頭4バイトからビッグエンディアンで`u32`を読み込む。長さが足りない場合は`None`を返す。 fn read_u32(slice: &[u8]) -> Option<u32> { let bytes = slice.first_chunk()?; // Rust 1.76までは少し面倒な書き方が必要だった // let bytes: &[u8; 4] = slice.get(..std::mem::size_of::<u32>())?.try_into().unwrap(); Some(u32::from_be_bytes(*bytes)) } assert_eq!(read_u32(&[0x01]), None); assert_eq!(read_u32(&[0x01, 0x02, 0x03, 0x04, 0x05]), Some(0x01020304));
フィールドの相対位置を取得できるようになった
std::mem::offset_of!
マクロにより、構造体の先頭からフィールドまでのバイト数が分かるようになりました。
ただし、#[repr(C)]
ではない構造体の数値は変わる可能性があることに注意が必要です。
#[repr(C)] struct ReprC { x: u8, y: u8, z: u32, } struct Rust { x: u8, y: u8, z: u32, r: ReprC, } fn main() { // 数値は変わらない(アーキテクチャ等では変わる) assert_eq!(0, std::mem::offset_of!(ReprC, x)); assert_eq!(1, std::mem::offset_of!(ReprC, y)); assert_eq!(4, std::mem::offset_of!(ReprC, z)); // 執筆時点のRust Playgroundでは4、5、0が表示されるが、今後この数値は変わる可能性がある println!("{}", std::mem::offset_of!(Rust, x)); println!("{}", std::mem::offset_of!(Rust, y)); println!("{}", std::mem::offset_of!(Rust, z)); // 現時点では入れ子での使用はできない // println!("{}", std::mem::offset_of!(Rust, r.x)); }
最近のrust-analyzer
最近rust-analyzerに入った変更の中から個人的に気になったものをピックアップしました。
代入文からlet-elseに変換できるようになった
2024-02-12での変更です。
Option
やResult
などTry
トレイトを実装している型を代入する文があるとき、その部分でlet-else
文に変換することができるようになりました。
一旦代入する文を書いておくことでSome
などの記述を省略することができるので便利そうです。
use文の名称変更では別名を作るようになった
2024-02-26での変更です。
use
文でインポートしている識別子をリネームすると、その識別子の定義を変えるのではなく、use
文中でas
を用いた別名が作られるようになりました。
これはあくまでuse
文でのリネームだけであり、使用部分でリネームをすると定義が変わります。
mod hoge { pub struct X; } use hoge::X; // ^ // このXをYにリネームすると // use hoge::X as Y; // となる type Hoge = X; // ^ // このXをYにリネームするとmod内の定義まで変わる
https://github.com/rust-lang/rust-analyzer/pull/16489
トレイトのかざしヒントに関連アイテムが表示されるようになった
2024-03-11での変更です。
トレイト上でかざし(hover)ヒントを表示させるときに設定した数だけトレイトの関連アイテムが表示されるようになりました。
既定では無効化されています。
rust-analyzer.hover.show.traitAssocItems
に整数を設定することで、その数だけアイテムを表示させることが出来ます。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
array::each_ref
#[stable(feature = "array_methods", since = "1.77.0")] pub fn each_ref(&self) -> [&T; N] { /* 実装は省略 */ } }
各要素を借用し、self
と同じ大きさである参照の配列を返す。
サンプル
let floats = [3.1, 2.7, -1.0]; let float_refs: [&f64; 3] = floats.each_ref(); assert_eq!(float_refs, [&3.1, &2.7, &-1.0]);
このメソッドは、map
のようなメソッドと組み合わせると特に便利である。
この場合、元の要素が[Copy
]でない場合に元々の配列がムーブすることを回避できる。
let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()]; let is_ascii = strings.each_ref().map(|s| s.is_ascii()); assert_eq!(is_ascii, [true, false, true]); // まだ元々の配列を使用できる。ムーブされていない assert_eq!(strings.len(), 3);
array::each_mut
#[stable(feature = "array_methods", since = "1.77.0")] pub fn each_mut(&mut self) -> [&mut T; N] { /* 実装は省略 */ } }
各要素を可変借用し、self
と同じ大きさである参照の配列を返す。
サンプル
let mut floats = [3.1, 2.7, -1.0]; let float_refs: [&mut f64; 3] = floats.each_mut(); *float_refs[0] = 0.0; assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]); assert_eq!(floats, [0.0, 2.7, -1.0]);
float::round_ties_even
impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f32 { /* 実装は省略 */ } }
数値に最も近い整数を返す。中間の数値の場合、最下位の桁が偶数に丸められる。
サンプル
let f = 3.3_f32; let g = -3.3_f32; let h = 3.5_f32; let i = 4.5_f32; assert_eq!(f.round_ties_even(), 3.0); assert_eq!(g.round_ties_even(), -3.0); assert_eq!(h.round_ties_even(), 4.0); assert_eq!(i.round_ties_even(), 4.0);
offset_of!
#[cfg(not(bootstrap))] #[stable(feature = "offset_of", since = "1.77.0")] #[allow_internal_unstable(builtin_syntax, hint_must_use)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /* 実装は省略 */ }
指定された型において、先頭からフィールドまでのバイト単位でのオフセットへ展開する。
構造体、列挙型、共用体、及びタプルに対応する。
入れ子になったフィールドへのアクセスは使用できるが、配列のインデックスは使用できない。
列挙型のバリアントはフィールドであるかのように記述できるものの、 バリアントそれ自体にオフセットがあるわけではない。
ただし、安定版では単体のフィールド名のみに対応しており、列挙型への対応は封鎖されている。
可視性が考慮される。すなわちすべての型とフィールドは呼び出し元から可視でなくてはならない。
mod nested { #[repr(C)] pub struct Struct { private: u8, } } // assert_eq!(mem::offset_of!(nested::Struct, private), 0); // ^^^ error[E0616]: field `private` of struct `Struct` is private
型の構造は、一般的に変更される可能性があり、またプラットフォーム固有
である。
もし構造が安定している必要がある場合は明示的なrepr
属性の使用を検討されたい。
Rustは指定された型における指定されたフィールドのオフセットがプログラムの寿命内で変わらないことを保証する。 ただし、同じプログラムであっても別にコンパイルした場合には異なる構造となる可能性がある。 また単一プログラムの実行内であっても、同様ではあるが同一ではない型についての保証はない。 例えば・・・
struct Wrapper<T, U>(T, U); type A = Wrapper<u8, u8>; type B = Wrapper<u8, i8>; // `u8`と`i8`は同じ構造であるとは言え、必ずしも同一とは言えない // Not necessarily identical even though `u8` and `i8` have the same layout! // assert!(mem::offset_of!(A, 1), mem::offset_of!(B, 1)); #[repr(transparent)] struct U8(u8); type C = Wrapper<u8, U8>; // `u8`と`U8`は同じ構造であるとは言え、必ずしも同一とは言えない // Not necessarily identical even though `u8` and `U8` have the same layout! // assert!(mem::offset_of!(A, 1), mem::offset_of!(C, 1)); struct Empty<T>(core::marker::PhantomData<T>); // `PhantomData`が常に同じ構造であるとは言え、必ずしも同一とは言えない // assert!(mem::offset_of!(Empty<u8>, 0), mem::offset_of!(Empty<i8>, 0));
サンプル
#![feature(offset_of_enum, offset_of_nested)] use std::mem; #[repr(C)] struct FieldStruct { first: u8, second: u16, third: u8 } assert_eq!(mem::offset_of!(FieldStruct, first), 0); assert_eq!(mem::offset_of!(FieldStruct, second), 2); assert_eq!(mem::offset_of!(FieldStruct, third), 4); #[repr(C)] struct NestedA { b: NestedB } #[repr(C)] struct NestedB(u8); assert_eq!(mem::offset_of!(NestedA, b.0), 0); #[repr(u8)] enum Enum { A(u8, u16), B { one: u8, two: u16 }, } assert_eq!(mem::offset_of!(Enum, A.0), 1); assert_eq!(mem::offset_of!(Enum, B.two), 2); assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
slice::first_chunk
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> { /* 実装は省略 */ } }
スライスから最初のN
個の項目への配列参照を返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let u = [10, 40, 30]; assert_eq!(Some(&[10, 40]), u.first_chunk::<2>()); let v: &[i32] = &[10]; assert_eq!(None, v.first_chunk::<2>()); let w: &[i32] = &[]; assert_eq!(Some(&[]), w.first_chunk::<0>());
slice::first_chunk_mut
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { /* 実装は省略 */ } }
スライスから最初のN
個の項目への可変な配列参照を返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &mut [0, 1, 2]; if let Some(first) = x.first_chunk_mut::<2>() { first[0] = 5; first[1] = 4; } assert_eq!(x, &[5, 4, 2]); assert_eq!(None, x.first_chunk_mut::<4>());
slice::split_first_chunk
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> { /* 実装は省略 */ } }
スライスから最初のN
個の項目への配列参照と、残りのスライスを返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &[0, 1, 2]; if let Some((first, elements)) = x.split_first_chunk::<2>() { assert_eq!(first, &[0, 1]); assert_eq!(elements, &[2]); } assert_eq!(None, x.split_first_chunk::<4>());
slice::split_first_chunk_mut
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] pub const fn split_first_chunk_mut<const N: usize>( &mut self, ) -> Option<(&mut [T; N], &mut [T])> { /* 実装は省略 */ } }
スライスから最初のN
個の項目への可変な配列参照と、残りのスライスを返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &mut [0, 1, 2]; if let Some((first, elements)) = x.split_first_chunk_mut::<2>() { first[0] = 3; first[1] = 4; elements[0] = 5; } assert_eq!(x, &[3, 4, 5]); assert_eq!(None, x.split_first_chunk_mut::<4>());
slice::last_chunk
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> { /* 実装は省略 */ } }
スライスから最後のN
個の項目への配列参照を返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let u = [10, 40, 30]; assert_eq!(Some(&[40, 30]), u.last_chunk::<2>()); let v: &[i32] = &[10]; assert_eq!(None, v.last_chunk::<2>()); let w: &[i32] = &[]; assert_eq!(Some(&[]), w.last_chunk::<0>());
slice::last_chunk_mut
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { /* 実装は省略 */ } }
スライスから最後のN
個の項目への可変な配列参照を返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &mut [0, 1, 2]; if let Some(last) = x.last_chunk_mut::<2>() { last[0] = 10; last[1] = 20; } assert_eq!(x, &[0, 10, 20]); assert_eq!(None, x.last_chunk_mut::<4>());
slice::split_last_chunk
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> { /* 実装は省略 */ } }
スライスから最後のN
個の項目への配列参照と、残りのスライスを返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &[0, 1, 2]; if let Some((elements, last)) = x.split_last_chunk::<2>() { assert_eq!(elements, &[0]); assert_eq!(last, &[1, 2]); } assert_eq!(None, x.split_last_chunk::<4>());
slice::split_last_chunk_mut
impl<T> [T] { #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] pub const fn split_last_chunk_mut<const N: usize>( &mut self, ) -> Option<(&mut [T], &mut [T; N])> { /* 実装は省略 */ } }
スライスから最後のN
個の項目への可変な配列参照と、残りのスライスを返す。
もしスライスの長さがN
未満の場合、このメソッドはNone
を返す。
サンプル
let x = &mut [0, 1, 2]; if let Some((elements, last)) = x.split_last_chunk_mut::<2>() { last[0] = 3; last[1] = 4; elements[0] = 5; } assert_eq!(x, &[5, 3, 4]); assert_eq!(None, x.split_last_chunk_mut::<4>());
slice::chunk_by
impl<T> [T] { #[stable(feature = "slice_group_by", since = "1.77.0")] #[inline] pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F> where F: FnMut(&T, &T) -> bool, { /* 実装は省略 */ } }
スライスから述語関数を使用して要素を区切り、重複しない要素の連続部分を生成するイテレータを返す。
述語は連続する要素のペアごとに呼び出される。つまり、slice[0]
とslice[1]
、その後にslice[1]
とslice[2]
、
その後も同じように続く。
サンプル
let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = slice.chunk_by(|a, b| a == b); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), None);
このメソッドはソートされた部分スライスを抽出するために使用できる。
let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4]; let mut iter = slice.chunk_by(|a, b| a <= b); assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..])); assert_eq!(iter.next(), Some(&[2, 3][..])); assert_eq!(iter.next(), Some(&[2, 3, 4][..])); assert_eq!(iter.next(), None);
slice::chunk_by_mut
impl<T> [T] { #[stable(feature = "slice_group_by", since = "1.77.0")] #[inline] pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F> where F: FnMut(&T, &T) -> bool, { /* 実装は省略 */ } }
スライスから述語関数を使用して要素を区切り、重複しない要素の可変な連続部分を生成するイテレータを返す。
述語は連続する要素のペアごとに呼び出される。つまり、slice[0]
とslice[1]
、その後にslice[1]
とslice[2]
、
その後も同じように続く。
サンプル
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = slice.chunk_by_mut(|a, b| a == b); assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next(), Some(&mut [3, 3][..])); assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); assert_eq!(iter.next(), None);
このメソッドはソートされた部分スライスを抽出するために使用できる。
let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4]; let mut iter = slice.chunk_by_mut(|a, b| a <= b); assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..])); assert_eq!(iter.next(), Some(&mut [2, 3][..])); assert_eq!(iter.next(), Some(&mut [2, 3, 4][..])); assert_eq!(iter.next(), None);
File::create_new
impl File { #[stable(feature = "file_create_new", since = "1.77.0")] pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> { /* 実装は省略 */ } }
読み書きモードでファイルを生成する。ファイルが既に存在する場合はエラーとなる。
この関数はファイルが存在しない場合にファイルを作成し、ファイルが存在する場合はエラーを返す。 この方法で呼び出しが成功した場合、返されるファイルは新しいものであることが保証される。
この選択肢は原子的であることから便利である。そうでない場合、ファイルが存在することを確認した上で新しいファイルを作成することになるが、 別のプロセスによってファイルが生成される可能性がある(TOCTOU競合条件・攻撃)。
これはFile::options().read(true).write(true).create_new(true).open(...)
のように書くこともできる。
サンプル
use std::fs::File; use std::io::Write; fn main() -> std::io::Result<()> { let mut f = File::create_new("hoge.txt")?; f.write_all("Hello, world!".as_bytes())?; Ok(()) }
Mutex::clear_poision
impl<T: ?Sized> Mutex<T> { #[inline] #[stable(feature = "mutex_unpoison", since = "1.77.0")] pub fn clear_poison(&self) { /* 実装は省略 */ } }
ミューテックスの中毒状態を解除する。
ミューテックスが中毒状態にある場合、この関数が呼ばれるまで中毒状態が維持される。これにより中毒状態から回復し、 また回復したと示すことができる。例えば値が既知の良好な値で上書きされる場合、ミューテックスを中毒解除することができる。 また、値を検査し一貫した状態であることを判断した上で中毒を解除することもできるだろう。
サンプル
use std::sync::{Arc, Mutex}; use std::thread; let mutex = Arc::new(Mutex::new(0)); let c_mutex = Arc::clone(&mutex); let _ = thread::spawn(move || { let _lock = c_mutex.lock().unwrap(); panic!(); // ミューテックスが中毒状態になる }).join(); assert_eq!(mutex.is_poisoned(), true); let x = mutex.lock().unwrap_or_else(|mut e| { **e.get_mut() = 1; mutex.clear_poison(); e.into_inner() }); assert_eq!(mutex.is_poisoned(), false); assert_eq!(*x, 1);
RwLock::clear_poision
impl<T: ?Sized> RwLock<T> { #[inline] #[stable(feature = "mutex_unpoison", since = "1.77.0")] pub fn clear_poison(&self) { /* 実装は省略 */ } }
ロックの中毒状態を解除する。
ロックが中毒状態にある場合、この関数が呼ばれるまで中毒状態が維持される。これにより中毒状態から回復し、 また回復したと示すことができる。例えば値が既知の良好な値で上書きされる場合、ロックを中毒解除することができる。 また、値を検査し一貫した状態であることを判断した上で中毒を解除することもできるだろう。
サンプル
use std::sync::{Arc, RwLock}; use std::thread; let lock = Arc::new(RwLock::new(0)); let c_lock = Arc::clone(&lock); let _ = thread::spawn(move || { let _lock = c_lock.write().unwrap(); panic!(); // ロックが中毒状態になる }).join(); assert_eq!(lock.is_poisoned(), true); let guard = lock.write().unwrap_or_else(|mut e| { **e.get_mut() = 1; lock.clear_poison(); e.into_inner() }); assert_eq!(lock.is_poisoned(), false); assert_eq!(*guard, 1);
変更点リスト
言語
- 網羅性を確認するために定義内での存在型を明示
- C文字列リテラルを安定化
- THIRでのunsafe確認を安定化
- 可変staticへの参照を警告するためのリント
static_mut_refs
を追加 - (間接参照限定で)非同期再帰呼び出しに対応
- リント
unstable_features
の非推奨化を取りやめ、コンパイラで使用するようにした - 一貫性(coherence)での帰納的サイクルを常に曖昧化
- 定数評価のインターン化で型駆動の記述を排除し、 現在は将来の互換性リントとなった
- let-elseでの波括弧型マクロ呼び出しを禁止
コンパイラ
- 将来の破損報告にリント
soft_unstable
を含める - x86に基づくプラットフォームにおいて
i128
とu128
が16バイトにアラインされるようにした - 診断出力で
--verbose
を使用 - 表示されるトークン間の空白を改善
unused_tuple_struct_fields
リントをdead_code
に統合- 適格性検査における不正な暗黙的境界でエラーを出力。 ただしBevy向けには例外を一時的に設ける
- 非ASCIIなソースコード向けカバレッジ計装・報告を修正
fn
・const
での暗黙的境界と適格性検査を修正riscv32{im|imafc}-unknown-none-elf
ターゲットをティア2に昇格- 新しいティア3ターゲットをいくつか追加
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
安定化されたAPI
array::each_ref
array::each_mut
core::net
f32::round_ties_even
f64::round_ties_even
mem::offset_of!
slice::first_chunk
slice::first_chunk_mut
slice::split_first_chunk
slice::split_first_chunk_mut
slice::last_chunk
slice::last_chunk_mut
slice::split_last_chunk
slice::split_last_chunk_mut
slice::chunk_by
slice::chunk_by_mut
Bound::map
File::create_new
Mutex::clear_poison
RwLock::clear_poison
Cargo
cargo::
によりビルド指示構文を拡張- メタデータの
id
形式をPackageIDSpec
として安定化 cargo-util-schemas
をクレートとして取り出し- デバッグ情報が要求されていない場合は全デバッグ情報を除去
- 全種類のランナーに対して環境からジョブサーバーを継承
- rustcプラグインへの対応を非推奨化
Rustdoc
その他
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。
関連リンク
さいごに
次のリリースのRust 1.78は5/3(金)にリリースされる予定です。 Rust 1.78ではトレイトが実装されていない際のエラーメッセージをカスタマイズできるようになる予定です。