本日2/9(金)にリリースされたRust 1.76の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
OptionやResultの値をのぞき見られるようになった
Option::inspect
やResult::inspect
、Result::inspect_err
により、値を消費せず中の値をのぞき見ることができるようになりました。
これまで、Option
やResult
の中の値を見るためにはif let
などで束縛したり無意味なmap
を使ったりしていました。
今回追加されたinspect{,_err}
により簡単にできるようになりました。
これはprintln!
を一行で仕込めるためデバッグ用にとても便利です。
// デバッグ用にSomeの時だけ中を見たい fn without_inspect(x: Option<String>) -> String { // これまでは無意味なmapを使ったりしていた x .map(|x| { println!("{x}"); x }) .map(|x| format!("foo: {x}")) .unwrap_or_else(|| "hoge".to_string()) } fn with_inspect(x: Option<String>) -> String { // Rust 1.76からはinspectにより簡単に書ける x .inspect(|x| eprintln!("{x}")) .map(|x| format!("bar: {x}")) .unwrap_or_else(|| "fuga".to_string()) }
最近のrust-analyzer
最近rust-analyzerに入った変更の中から個人的に気になったものをピックアップしました。
二重if文をひとつにまとめられるようになった
2024-01-15 (v0.3.1807)での変更です。
if
文が二重になっているとき、1つ目のif
文にカーソルがある際のコードアクションでif
文を1つにまとめられるようになりました。
リファクタリングの時に便利ですね。
リテラルを別の表現で見られるようになった
2024-01-22 (v0.3.1815)での変更です。
文字列や数値リテラルでのかざし(hover)ヒントで、その値の別表現を見ることができるようになりました。
文字列の場合はエスケープ等が展開された実際の値が、整数の場合は10進・16進・2進での表現が、 小数の場合は実際の精度に合わせた値と16進表現が確認できます。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
Arc::unwrap_or_clone
#[inline] #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")] pub fn unwrap_or_clone(this: Self) -> T { /* 実装は省略 */ } }
T
に対する参照が1つだけの場合、それを展開する。そうでない場合、T
を複製して返す。
arc_t
が型Ac<T>
のとき、この関数は(*arc_t).clone()
と等価であるが、可能な限り内部の値を複製しない。
サンプル
let inner = String::from("test"); let ptr = inner.as_ptr(); let arc = Arc::new(inner); let inner = Arc::unwrap_or_clone(arc); // 内部の値は複製されない assert!(ptr::eq(ptr, inner.as_ptr())); let arc = Arc::new(inner); let arc2 = arc.clone(); let inner = Arc::unwrap_or_clone(arc); // 2つの参照があるため内部の値の複製が必要 assert!(!ptr::eq(ptr, inner.as_ptr())); // `arc2`は最後の参照なので、展開することで元の`String`が得られる let inner = Arc::unwrap_or_clone(arc2); assert!(ptr::eq(ptr, inner.as_ptr()));
use std::{ptr, sync::Arc}; let inner = String::from("test"); let ptr = inner.as_ptr(); let arc = Arc::new(inner); let inner = Arc::unwrap_or_clone(arc); // 内部の値は複製されない assert!(ptr::eq(ptr, inner.as_ptr())); let arc = Arc::new(inner); let arc2 = arc.clone(); let inner = Arc::unwrap_or_clone(arc); // 2つの参照があるため内部の値の複製が必要 assert!(!ptr::eq(ptr, inner.as_ptr())); // `arc2`は最後の参照なので、展開することで元の`String`が得られる let inner = Arc::unwrap_or_clone(arc2); assert!(ptr::eq(ptr, inner.as_ptr()));
Rc::unwrap_or_clone
#[inline] #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")] pub fn unwrap_or_clone(this: Self) -> T { /* 実装は省略 */ } }
T
に対する参照が1つだけの場合、それを展開する。そうでない場合、T
を複製して返す。
rc_t
が型Rc<T>
のとき、この関数は(*rc_t).clone()
と等価であるが、可能な限り内部の値を複製しない。
サンプル
let inner = String::from("test"); let ptr = inner.as_ptr(); let rc = Rc::new(inner); let inner = Rc::unwrap_or_clone(rc); // 内部の値は複製されない assert!(ptr::eq(ptr, inner.as_ptr())); let rc = Rc::new(inner); let rc2 = rc.clone(); let inner = Rc::unwrap_or_clone(rc); // 2つの参照があるため内部の値の複製が必要 assert!(!ptr::eq(ptr, inner.as_ptr())); // `rc2`は最後の参照なので、展開することで元の`String`が得られる let inner = Rc::unwrap_or_clone(rc2); assert!(ptr::eq(ptr, inner.as_ptr()));
use std::{ptr, rc::Rc}; let inner = String::from("test"); let ptr = inner.as_ptr(); let rc = Rc::new(inner); let inner = Rc::unwrap_or_clone(rc); // 内部の値は複製されない assert!(ptr::eq(ptr, inner.as_ptr())); let rc = Rc::new(inner); let rc2 = rc.clone(); let inner = Rc::unwrap_or_clone(rc); // 2つの参照があるため内部の値の複製が必要 assert!(!ptr::eq(ptr, inner.as_ptr())); // `rc2`は最後の参照なので、展開することで元の`String`が得られる let inner = Rc::unwrap_or_clone(rc2); assert!(ptr::eq(ptr, inner.as_ptr()));
Result::inspect
#[inline] #[stable(feature = "result_option_inspect", since = "1.76.0")] pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self { /* 実装は省略 */ } }
(値がOk
の場合に)内部の値への参照を渡してクロージャを呼び出す。
サンプル
let x: u8 = "4" .parse::<u8>() .inspect(|x| println!("元の値:{x}")) .map(|x| x.pow(3)) .expect("数値をパースできない");
Result::inspect_err
#[inline] #[stable(feature = "result_option_inspect", since = "1.76.0")] pub fn inspect_err<F: FnOnce(&E)>(self, f: F) -> Self { /* 実装は省略 */ } }
(値がErr
の場合に)内部の値への参照を渡してクロージャを呼び出す。
サンプル
use std::{fs, io}; fn read() -> io::Result<String> { fs::read_to_string("address.txt") .inspect_err(|e| eprintln!("ファイル読み込みに失敗:{e}")) }
Option::inspect
#[inline] #[stable(feature = "result_option_inspect", since = "1.76.0")] pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self { /* 実装は省略 */ } }
(値がSome
の場合に)内部の値への参照を渡してクロージャを呼び出す。
サンプル
let v = vec![1, 2, 3, 4, 5]; // "値:4"を出力 let x: Option<&usize> = v.get(3).inspect(|x| println!("値:{x}")); // 何も出力しない let x: Option<&usize> = v.get(5).inspect(|x| println!("値:{x}"));
type_name_of_val
#[must_use] #[stable(feature = "type_name_of_val", since = "1.76.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str { /* 実装は省略 */ }
指す値の型名を文字列スライスとして返す。
これはtype_name::<T>()
と同じだが、変数の型を簡単に得られない場合に使用できる。
メモ
type_name
と同様これは診断用でしかなく、正確な出力が保証されるものではない。
最善努力で結果を提供するものの、出力はコンパイラのバージョン間で変更される可能性がある。
つまり、デバッグ用であることからプログラムの動作に影響を与えるような出力の使用は避けられたい。
詳細はtype_name
を参照のこと。
なお、この関数はトレイトオブジェクトを解決しない。
つまりtype_name_of_val(&7u32 as &dyn Debug)
は、ここでは"u32"
ではなく"dyn Debug"
を返すだろう。
サンプル
既定の整数型、浮動小数点数型を表示する。
use std::any::type_name_of_val; let s = "hoge"; let x: i32 = 1; let y: f32 = 1.0; assert!(type_name_of_val(&s).contains("str")); assert!(type_name_of_val(&x).contains("i32")); assert!(type_name_of_val(&y).contains("f32"));
ptr::from_mut
#[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] #[rustc_const_stable(feature = "ptr_from_ref", since = "1.76.0")] #[rustc_allow_const_fn_unstable(const_mut_refs)] #[rustc_never_returns_null_ptr] pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T { /* 実装は省略 */ }
可変参照を生のポインタに変換する。
これはr as *mut T
と同等だが、型や可変性を変えない分、
特にコードがリファクタリングされた場合などに幾分か安全である。
ptr::from_ref
#[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] #[rustc_const_stable(feature = "ptr_from_ref", since = "1.76.0")] #[rustc_never_returns_null_ptr] #[rustc_diagnostic_item = "ptr_from_ref"] pub const fn from_ref<T: ?Sized>(r: &T) -> *const T { /* 実装は省略 */ }
参照を名前のポインタに変換する。
これはr as *const T
と同等だが、型や可変性を変えない分、
特にコードがリファクタリングされた場合などに幾分か安全である。
ptr::addr_eq
#[stable(feature = "ptr_addr_eq", since = "1.76.0")] #[inline(always)] #[must_use = "pointer comparison produces a value"] pub fn addr_eq<T: ?Sized, U: ?Sized>(p: *const T, q: *const U) -> bool { /* 実装は省略 */ }
2つのポインタのアドレスが等しいかどうかを、太いポインタのメタデータを無視して比較する。
引数が同じ型の薄いポインタである場合、eq
と同義である。
サンプル
use std::ptr; let whole: &[i32; 3] = &[1, 2, 3]; let first: &i32 = &whole[0]; assert!(ptr::addr_eq(whole, first)); assert!(!ptr::eq::<dyn std::fmt::Debug>(whole, first));
変更点リスト
言語
コンパイラ
- ピン留めされた
#[must_use]
なポインタ(特にT
が#[must_use]
なBox<T>
)を、unused_must_use
で警告 - 健全性の修正:詰め込まれた構造体におけるサイズの無いフィールドでのオフセット計算を修正
- 健全性の修正:末尾がdyn Traitな詰め込まれた型での動的なサイズ・アライン計算ロジックを修正
- JSONの診断出力を区別するために
$message_type
フィールドを追加 - WindowsのEHContセキュリティ機能をRustで使えるようにした
- {x86_64,i686}-win7-windows-msvcをTier 3ターゲットとして追加
- aarch64-apple-watchosをTier 3ターゲットとして追加
- arm64e-apple-ios & arm64e-apple-darwinをTier 3ターゲットとして追加
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
dbg!()
に列番号を追加std::hash::{DefaultHasher, RandomState}
のエクスポートを追加- fmtにおける指数の丸め問題を修正
RwLockReadGuard
とRwLockWriteGuard
のDebug実装にT: ?Sizedを追加- Windows:隠しファイルに対する
File::create
が動作するようにした
安定化されたAPI
Arc::unwrap_or_clone
Rc::unwrap_or_clone
Result::inspect
Result::inspect_err
Option::inspect
type_name_of_val
std::hash::{DefaultHasher, RandomState}
これまで、これらはstd::collections::hash_map
経由でのみ使うことが出来たptr::{from_ref, from_mut}
ptr::addr_eq
Cargo
Cargoのリリースメモ(※訳注:英語ページ)を参照。
Rustdoc
- 再エクスポート用にcfgとdoc(cfg)属性をマージしない
- rustdoc:サイドバーやトップバーをリサイズ及び不可視化可能に
- rustdoc-search:トレイトと関連型に対応
- rustdoc:アイテムの宣言でコメントに強調表示を追加
互換性メモ
- ユニット型の束縛用リントを既定許可で追加。
これは将来のRustリリースでは既定で警告へ格上げされる予定である。
マクロの中には
()
型の束縛を、ユーザーが指定したスパンで生成するものがある - x86_64-sun-solarisターゲットを削除
- asmjs-unknown-emscriptenターゲットを削除
- 環境変数によって継承されたジョブサーバーのエラーを報告。 これは良性の問題についても 警告する可能性がある
- 最小の外部LLVMに更新
print_tts
を改善。 この変更により、手続きマクロにおいて.to_string()
のあとに(任意のRustコードでなく)特定の構造体を期待する、 トークンツリーを単純に手動でパースするコードが破壊される可能性があるIMPLIED_BOUNDS_ENTAILMENT
をリントからコンパイルエラーに変更- 一部のイテレーターをcollectする際の、Vecにおけるメモリ確保の動作が変更された。
現在メモリ確保の動作は規定されているわけではないが、とは言え驚くような変更かもしれない。
詳細は
impl FromIterator for Vec
を参照 - 独立した定数における
default
を正常に拒絶
関連リンク
さいごに
Rust 1.76での安定化が予定化されていたC文字列リテラル(c"hoge"
)ですが、文字列中にNUL文字を含められるということで先送りされてしまいました。
その問題は既に修正されており、Rust 1.77で使えるようになる予定です。
次のリリースのRust 1.77は3/22(金)にリリースされる予定です。 Rust 1.77では前述の通りC文字列リテラルが使えるようになったり、スライスから配列を切り出せるようになったり、 非同期関数での再帰呼び出しが(条件付きで)できるようになったりする予定です。