こんにちは、あずんひ(@aznhe21)です。 会社を辞めて無職になったので、今後深掘りシリーズは個人ブログからお届けします。
さて、本日5/20(金)にRust 1.61がリリースされました。 この記事ではRust 1.61での変更点を詳しく紹介します。
- ピックアップ
- 安定化されたAPIのドキュメント
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
標準入出力のハンドルが扱いやすくなった
std::io::{stdin, stdout, stderr}
のlock
メソッドを呼び出す際、別の変数に束縛する必要がなくなり使い勝手が向上しました。
これまでライフタイムが入っていた部分には'static
が入るようになり、StdoutLock<'a>
ではなくStdoutLock<'static>
といった形で返されるようになりました。
use std::io::Write; fn main() { // Rust 1.60以前 // let stdout = std::io::stdout(); // let mut stdout = stdout.lock(); // Rust 1.61以降 let mut stdout = std::io::stdout().lock(); stdout.write_all(b"Hello, world!\n").unwrap(); }
mainの戻り値で終了コードを返せるようになった
main
関数から、std::process::ExitCode
を使って任意の終了コードを返せるようになりました。
これまでもResult<T, E>
などを返してEXIT_SUCCESS
とEXIT_FAILURE
を返す、
またはstd::process::exit
によって任意の終了コードを返すことができましたが、使い勝手がよくありませんでした。
use std::process::ExitCode; fn main() -> ExitCode { // 終了コードが42になる 42.into() }
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
Pin::static_mut
impl<T: ?Sized> Pin<&'static mut T> { #[stable(feature = "pin_static_ref", since = "1.61.0")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { /* 実装は省略 */ } }
staticな可変参照からピン留めされた可変参照を得る。
T
は'static
ライフタイムで借用されていて寿命が尽きることはないため、この操作は安全である。
Pin::static_ref
impl<T: ?Sized> Pin<&'static T> { #[stable(feature = "pin_static_ref", since = "1.61.0")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { /* 実装は省略 */ } }
staticな参照からピン留めされた参照を得る。
T
は'static
ライフタイムで借用されていて寿命が尽きることはないため、この操作は安全である。
Vec::retain_mut
impl<T, A: Allocator> Vec<T, A> { #[stable(feature = "vec_retain_mut", since = "1.61.0")] pub fn retain_mut<F>(&mut self, mut f: F) where F: FnMut(&mut T) -> bool, { /* 実装は省略 */ } }
指定された述語関数によって指定された要素のみを保持する。述語関数には可変参照を渡す。
つまり、e
についてf(&mut e)
がfalse
を返すような要素すべてを削除する。
このメソッドは値を直接操作であり、各要素を元の順序で必ず1回ずつ走査し、保持された要素の順序も保持される。
サンプル
let mut vec = vec![1, 2, 3, 4]; vec.retain_mut(|x| if *x > 3 { false } else { *x += 1; true }); assert_eq!(vec, [2, 3, 4]);
VecDeque::retain_mut
impl<T, A: Allocator> VecDeque<T, A> { #[stable(feature = "vec_retain_mut", since = "1.61.0")] pub fn retain_mut<F>(&mut self, mut f: F) where F: FnMut(&mut T) -> bool, { /* 実装は省略 */ } }
指定された述語関数によって指定された要素のみを保持する。述語関数には可変参照を渡す。
つまり、e
についてf(&mut e)
がfalse
を返すような要素すべてを削除する。
このメソッドは値を直接操作であり、各要素を元の順序で必ず1回ずつ走査し、保持された要素の順序も保持される。
サンプル
use std::collections::VecDeque; let mut buf = VecDeque::new(); buf.extend(1..5); buf.retain_mut(|x| if *x % 2 == 0 { *x += 1; true } else { false }); assert_eq!(buf, [3, 5]);
std::os::unix::net::SocketAddr::from_pathname
impl SocketAddr { #[stable(feature = "unix_socket_creation", since = "1.61.0")] pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr> where P: AsRef<Path>, { /* 実装は省略 */ } }
AF_UNIX
と指定されたパスによってSockAddr
を構築する。
エラー
パスがSUN_LEN
より長いかNULLバイトを含む場合はエラーが返る。
サンプル
use std::os::unix::net::SocketAddr; use std::path::Path; let address = SocketAddr::from_pathname("/path/to/socket").unwrap(); assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket")));
NULLバイトを使ってSocketAddr
を生成するとエラーが発生する。
use std::os::unix::net::SocketAddr; assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err());
std::process::ExitCode
#[derive(Clone, Copy, Debug)] #[stable(feature = "process_exitcode", since = "1.61.0")] pub struct ExitCode( /* フィールドは省略 */ );
この型は、現在のプロセスが正常終了する際に親プロセスに返されるステータスコードを表す。
ExitCode
は標準ライブラリから([Termination::report()
]によって)使われることを想定されており、
PartialEq
やEq
、Hash
などのアクセサは意図的に提供しない。
標準ライブラリは標準的なSUCCESS
やFAILURE
といった終了コードの他に、From<u8> for ExitCode
による任意の終了コードの構築も提供する。
可搬性
この型で使用される数値は可搬であるとは言えず、他のプラットフォームではマスクする量も異なる可能性がある。
プラットフォームの標準的な成功・不成功コードは
関連アイテムSUCCESS
とFAILURE
を参照されたい。
ExitStatus
との違い
ExitCode
はTermination
トレイトを通じて現在のプロセスを終了させることを意図しているが、
[ExitStatus
]はと言うと、子プロセスを終了させることを意味する。
プラットフォームの互換性の違いと想定用途のため、これらのAPIは分けられている。
通常、子プロセスのExitStatus
を事後的に、現在のプロセスに対して正確に再現することはできない。
サンプル
ExitCode
はTermination
を実装するため、クレートのmain
関数から返すことができる。
use std::process::ExitCode; fn main() -> ExitCode { if !check_foo() { return ExitCode::from(42); } ExitCode::SUCCESS }
std::process::ExitCode::SUCCESS
#[stable(feature = "process_exitcode", since = "1.61.0")] impl ExitCode { #[stable(feature = "process_exitcode", since = "1.61.0")] pub const SUCCESS: ExitCode = ExitCode( /* 値は省略 */ ); }
このプラットフォームにおける、正常終了時の標準的なExitCode
。
()
を返すmain
は暗黙的に正常終了するため、他の考えられるコードを返すわけでもなければ、main
からこの値を返す必要が無いことに注意されたい。
std::process::ExitCode::FAILURE
#[stable(feature = "process_exitcode", since = "1.61.0")] impl ExitCode { #[stable(feature = "process_exitcode", since = "1.61.0")] pub const FAILURE: ExitCode = ExitCode( /* 値は省略 */ ); }
このプラットフォームにおける、異常終了時の標準的なExitCode
。
main
からこの値とSUCCESS
のみを返す場合、代わりにそれぞれErr(_)
とOk(())
を返すことも検討されたい。
これらは同じコードを返す(ただしエラーのeprintln!
もされる)。
std::process::Termination
#[cfg_attr(not(test), lang = "termination")] #[stable(feature = "termination_trait_lib", since = "1.61.0")] #[rustc_on_unimplemented( message = "`main` has invalid return type `{Self}`", label = "`main` can only return types that implement `{Termination}`" )] pub trait Termination { #[stable(feature = "termination_trait_lib", since = "1.61.0")] fn report(self) -> ExitCode; }
main
関数における任意の戻り値型が実装するトレイト。
Cのmain関数は戻り値型として整数を返すことのみサポートしている。そのため、Termination
トレイトを実装するすべての型は整数に変換されなければならない。
デフォルト実装は、正常に実行されたことを示すlibc::EXIT_SUCCESS
を返す。失敗時はlibc::EXIT_FAILURE
を返す。
main
関数の戻り値はランタイムごとに仕様が異なるため、このトレイトは便宜上、標準ライブラリのランタイムでのみ使用できる可能性がある。
その他のランタイムではこの様な機能の提供は必須ではない。
必須メソッド
fn report(self) -> ExitCode
値をステータスコードとして表現するために呼ばれる。このステータスコードはOSに返される。
std::thread::JoinHandle::is_finished
impl<T> JoinHandle<T> { #[stable(feature = "thread_is_running", since = "1.61.0")] pub fn is_finished(&self) -> bool { /* 実装は省略 */ } }
紐付いたスレッドにおいて主関数が実行を終了したかどうかを確認する。
このメソッドはスレッドの主関数が返ったあと、スレッド自体が実行を停止する前の短い期間でもtrue
を返す場合がある。
とは言え、このメソッドがtrue
を返せば、長時間のブロックもなくjoin
がすぐに返ることが期待できる。
この関数はブロックしない。スレッドの終了を待機する間ブロックする場合はjoin
を使用する。
変更点リスト
言語
const fn
のシグネチャにおいて、総称トレイトの境界を含められるようになったconst fn
のシグネチャにおいて、引数と戻り値にimpl Trait
が使えるようになったconst fn
において、関数ポインタの作成とキャスト、及び受け渡しができるようになった- 再帰的呼び出しにおいて、関数での不透明な
impl Trait
の戻り値型を設定できるようになった
コンパイラ
#[link]
属性とコマンドライン上でのリンク修飾構文、そしてwhole-archive
修飾がサポートされた- デバッグ情報において
char
型がUTF-32である旨を宣言 #[target_feature]
属性でaarch64の機能を使えるようになった- x86の
#[target_feature = "adx"]
が安定化された
ライブラリ
ManuallyDrop<T>
がT
と同じレイアウトであることを文書化- テスト実行時に
#[ignore = "…"]
のメッセージを表示 - Windowsの標準入出力ハンドルが提供されない場合でも、NULLハンドルとして一貫的に表示する
(※訳注:
#[windows_subsystem="windows"]
の場合でも標準入出力での読み書き時にエラーが発生しなくなる) std::io::stdio::lock()
の戻り値を'static
なハンドルにした。 これまでは、stdin/stdout/stderrのロック済みハンドルを生成するとロックされているハンドルを借用していたが、 これはlet out = std::io::stdout().lock();
と書くのを妨害していた。これはout
がstdout()
の戻り値よりも寿命が長くなってしまうためである。 この様なコードも動作するようになり、多くのRustユーザーに影響を与えていた落とし穴が解消された
→ピックアップVec::from_raw_parts
の入力について制約を緩和したstd::thread::available_parallelism
がcgroupのクォータを考慮するようになった。available_parallelism
は並列計算のスレッドプール生成によく使われるが、これはパフォーマンス上CPUを占有する可能性があるため、available_parallelism
は継続的に使用できるスレッド数と一致する値を可能な限り返す。 例えば8個の仮想CPUがあるコンテナで、クォータが50%の使用のみ許可しているの場合、available_parallelism
は4を返す
安定化されたAPI
Pin::static_mut
Pin::static_ref
Vec::retain_mut
VecDeque::retain_mut
Write
forCursor<[u8; N]>
std::os::unix::net::SocketAddr::from_pathname
std::process::ExitCode
とstd::process::Termination
。この2つのAPIの安定化により、プログラムはmain
から特殊な終了コードを使ってエラーを返せるようになるstd::thread::JoinHandle::is_finished
以下のAPIが定数文脈で使えるようになった。
<*const T>::offset
and<*mut T>::offset
<*const T>::wrapping_offset
and<*mut T>::wrapping_offset
<*const T>::add
and<*mut T>::add
<*const T>::sub
and<*mut T>::sub
<*const T>::wrapping_add
and<*mut T>::wrapping_add
<*const T>::wrapping_sub
and<*mut T>::wrapping_sub
<[T]>::as_mut_ptr
<[T]>::as_ptr_range
<[T]>::as_mut_ptr_range
Cargo
機能に変更は無いが、互換性メモを参照されたい。
互換性メモ
- 以前は場合によってネイティブな静的ライブラリが
whole-archive
でリンクされていたが、これからは明示的に要求されない限りrustcはwhole-archive
を使わないようになった。この変更によってリンクエラーが発生する場合がある。このエラーを修正するためには、コマンドラインやビルドスクリプト、または#[link]
属性でリンクされるネイティブなライブラリを……- (大抵) 依存関係を考慮してアイテムを並び替える(
a
がb
に依存している場合はb
より先にa
を並べる) - (まれ) または
+whole-archive
修飾子を使うよう更新する
- (大抵) 依存関係を考慮してアイテムを並び替える(
- Rustでのパニック時の掃除中に、FFIコードでの2回目の巻き戻しをキャッチした場合はプロセスを異常終了させるようになった
- 手続きマクロにおいて
ident
照合子がグループ化されなくなった - 生文字リテラル
r#
での#
の数が256未満であることが要求されるようになった - dyn型がトレイト境界を満たすかを確認する際、親トレイトの境界が強制されるようになった
cargo vendor
の--sync
フラグがそれぞれ1つの値のみ受け入れるようになったall()
やany()
内でのcfg
の述語が、短絡ではなく常に評価され、エラーが検出されるようになった。 ここで、all
の短絡評価する挙動を使って書かれたcfg(all(feature = "nightly", syntax-requiring-nightly))
のような Nightly限定のコードでは互換性の考慮事項が発生し、コンパイルに失敗するようになる。 代わりにcfg_attr(feature = "nightly", ...)
を使うか、cfg
をネストして使うこと- bootstrap:static-libstdcppがデフォルトで有効化され、llvm-toolsが有効のときに無効化できるようになった
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部での重要なパフォーマンス改善をもたらす。
関連リンク
さいごに
次のリリースのRust 1.62は7/1(金)にリリースされる予定です。 1.62ではLinuxでのMutexの実装がfutexベースのものになって軽量化・高速化したりするようです。