本日11/17(金)にリリースされたRust 1.74の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
演算を飽和させるためのSaturating型が使えるようになった
各種演算で飽和させる事ができるSaturating<T>型が使えるようになりました。
これまでは演算ごとにhoge.saturating_add(fuga)やfoo.saturating_mul(bar)といったメソッド呼び出しをする必要がありました。
これは一連の演算の中で1つだけあるというのであれば良いのですが、複数あると煩雑で読みやすさが激減します。
そこで値をSaturating<T>型に包む事で、自然な形の演算でも結果を飽和させることができるようになります。
両辺共にSaturating<T>の必要があるため、先に包んだものを代入しておくと良いでしょう。
use std::num::Saturating; // saturating_*メソッドを使う形 fn without_saturating(x: u32) -> u32 { let y = 100; let z = 200; x.saturating_mul(y).saturating_add(z) } // Saturating型を使う型 fn with_saturating(x: u32) -> u32 { let x = Saturating(x); let y = Saturating(100); let z = Saturating(200); (x * y + z).0 }
依存クレートのコンパイル失敗時にもコンパイルを極力続行可能に
cargoに--keep-goingフラグが追加され、依存関係における一部クレートのコンパイルに失敗しても、
(そのクレートに依存しない)他クレートのコンパイルを続行できるようになりました。
これはmakeの-k(--keep-going)に相当するもので、「エラーが出るのは分かっているが取り敢えずやれるだけコンパイルしておきたい」という場合に使えます。
Cargo.tomlにリントの設定を書けるようになった
lib.rsやmain.rsに#![allow(lint)]などを書く代わりに、Cargo.tomlの[lints]テーブルにリントの設定を書くことができるようになりました。
単体クレートの場合はそこまで威力はありませんが、ワークスペースの場合は設定を共有できるため便利です。
この設定はlintsテーブル以下に各ツールのテーブルを書きます。
例えばRust標準のリントはlints.rustに、Clippyのものはlints.clippyに書きます。
ツール名は、Rust標準以外は#[allow(tool::lint)]のtoolの部分に当たります。
また、ワークスペースで使う場合は他のフィールド同様、各クレートにlints.workspace = trueの記述が必要です。
各ルールはrule = "deny"のように書きます。
優先度も同時に指定するrule = { level = "deny", priority = -1 }という書き方もできます。
これはグループに指定するのが意図された使い方のようです。
例
単体クレートの場合
# Cargo.toml [package] name = "hoge" version = "0.1.0" edition = "2021" [lints.rust] dead_code = "deny"
ワークスペースの場合
# Cargo.toml
[workspace]
members = ["hoge"]
[workspace.lints.clippy]
all = { level = "allow", priority = -1 }
perf = "deny"
# hoge/Cargo.toml [package] name = "hoge" version = "0.1.0" edition = "2021" [lints] workspace = true
rustdocに警告用ブロックを書けるようになった
rustdocに目立つ警告用文言を書けるようになりました。
これはGitHub等で導入されているMarkdownの拡張文法ではなく、自分でHTMLタグ(<div class="warning">)を書く形式で実装されています。
拡張文法を採用しなかった点について、
個人的にはGitHubの拡張文法では引用でもないのに引用記法を流用している(当初は出力としてもblockquote要素が使われていた)のがどうにも嫌なため、
それに比べれば書きにくくはあるもののあべこべでない分こちらの方が好みです。
/// 色々やる関数。 /// /// <div class="warning">この文書は見本です。</div> pub fn hoge() {}
最近のrust-analyzer
最近rust-analyzerに入った変更の中から個人的に気になったものをピックアップしました。
変数への切り出しが範囲選択不要になった
2023-10-30 (v0.3.1713)での変更です。
関数呼び出しなどにカーソルがあるとき、その結果を変数に切り出すことができるようになりました。 これまでは関数呼び出し全体を範囲選択する必要があり、少し不便でした。
不変版トレイト実装から可変版実装を生成できるようになった
2023-11-06 (v0.3.1722)での変更です。
トレイトstd::ops::Indexの実装からトレイトstd::ops::IndexMutの実装を生成できるようになりました。
現段階ではIndexからIndexMutの生成だけですが、将来的にはそれ以外のトレイトも生成できるようになるでしょう。
定義へ飛ばずに構造体の中身を確認できるようになった
2023-11-13 (v0.3.1730)での変更です。
構造体のかざし(hover)ヒントで構造体のフィールドを確認できるようになりました。 定義に飛ばずとも内容が分かるため便利そうです。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
std::num::Saturating
#[stable(feature = "saturating_int_impl", since = "1.74.0")] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] #[repr(transparent)] #[rustc_diagnostic_item = "Saturating"] pub struct Saturating<T>( #[stable(feature = "saturating_int_impl", since = "1.74.0")] pub T, );
Tに対し、あえて飽和する演算を提供する。
u32の値に対する+のような演算は決してオーバーフローしないように設計されており、
同時に一部のデバッグ構成ではオーバーフローが検知されパニックする。
ほとんどの演算はこの部類ではあるが、中には飽和演算を明確に期待して頼るコードもある。
飽和演算はsaturating_addのようなメソッド、あるいはSaturating<T>型によって使用可能である。
後者は内包する値に対するあらゆる標準的な算術演算が飽和するという意味論である事を意図していると言える。
内包する値はSaturatingタプルの.0インデックスを使用して取得できる。
サンプル
use std::num::Saturating; let max = Saturating(u32::MAX); let one = Saturating(1u32); assert_eq!(u32::MAX, (max + one).0);
OsString::from_encoded_bytes_unchecked
#[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub unsafe fn from_encoded_bytes_unchecked(bytes: Vec<u8>) -> Self { /* 実装は省略 */ } }
バイト列を、OsStr用にエンコードされた有効なデータを含んでいるかどうかを確認せずにOsStringに変換する。
バイト列の符号化方式は未規定で、環境依存で、UTF-8の自己同期的スーパーセットである。 UTF-8の自己同期的スーパーセットであることから、この符号化方式は7ビットASCIIのスーパーセットでもある。
ネイティブ表現との安全でクロスプラットフォームな変換についてはモジュールの最上位文書を参照のこと。
安全性
符号化方式が未規定であるため、呼び出し元は検証済みUTF-8や同一のターゲットプラットフォーム向けにビルドされた
同一のRustバージョンにおけるOsStr::as_encoded_bytesから返されるバイト列の混合からなるバイト列を渡す必要がある。
例えばネットワーク経由で送信されたバイト列やファイルに保存されたバイト列からOsStringを再構築することは
この安全性規則に違反するだろう。
符号化方式が自己同期的であることにより、
OsStr::as_encoded_bytesから返されるバイト列は有効な空でないUTF-8の部分文字列の直前直後へ分割できる。
サンプル
use std::ffi::OsStr; let os_str = OsStr::new("Mary had a little lamb"); let bytes = os_str.as_encoded_bytes(); let words = bytes.split(|b| *b == b' '); let words: Vec<&OsStr> = words.map(|word| { // 安全性: // - 各`word`は`OsStr::as_encoded_bytes`に由来する内容のみを含む // - 空でないUTF-8の部分文字列であるASCIIの空白文字でのみ分割する unsafe { OsStr::from_encoded_bytes_unchecked(word) } }).collect();
OsString::into_encoded_bytes
#[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub fn into_encoded_bytes(self) -> Vec<u8> { /* 実装は省略 */ } }
OsStringからバイト列のスライスに変換する。バイト列のスライスをOsStringに戻すには、
OsStr::from_encoded_bytes_unchecked関数を使用する。
バイト列の符号化方式は未規定で、環境依存で、UTF-8の自己同期的スーパーセットである。 UTF-8の自己同期的スーパーセットであることから、この符号化方式は7ビットASCIIのスーパーセットでもある。
注意:符号化方式が未規定であるため、有効なUTF-8でないあらゆるバイト列のサブスライスはあやふやなものとして扱われるべきであり、
また同一のターゲットプラットフォーム向けにビルドされた同一のRustバージョン内でのみ比較されるべきである。
例えばネットワーク経由で送信されたバイト列やファイルに保存されたバイト列は非互換となる可能性が高い。
符号化方式の詳細についてはOsStringを、環境依存の指定された変換についてはstd::ffiを参照のこと。
OsStr::from_encoded_bytes_unchecked
#[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub unsafe fn from_encoded_bytes_unchecked(bytes: &[u8]) -> &Self { /* 実装は省略 */ } }
バイト列のスライスを、OsStr用にエンコードされた有効なデータを含んでいるかどうかを確認せずにOS文字列のスライスに変換する。
バイト列の符号化方式は未規定で、環境依存で、UTF-8の自己同期的スーパーセットである。 UTF-8の自己同期的スーパーセットであることから、この符号化方式は7ビットASCIIのスーパーセットでもある。
ネイティブ表現との安全でクロスプラットフォームな変換についてはモジュールの最上位文書を参照のこと。
Safety
符号化方式が未規定であるため、呼び出し元は検証済みUTF-8や同一のターゲットプラットフォーム向けにビルドされた
同一のRustバージョンにおけるOsStr::as_encoded_bytesから返されるバイト列の混合からなるバイト列を渡す必要がある。
例えばネットワーク経由で送信されたバイト列やファイルに保存されたバイト列からOsStrを再構築することは
この安全性規則に違反するだろう。
符号化方式が自己同期的であることにより、
OsStr::as_encoded_bytesから返されるバイト列は有効な空でないUTF-8の部分文字列の直前直後へ分割できる。
サンプル
use std::ffi::OsStr; let os_str = OsStr::new("Mary had a little lamb"); let bytes = os_str.as_encoded_bytes(); let words = bytes.split(|b| *b == b' '); let words: Vec<&OsStr> = words.map(|word| { // 安全性: // - 各`word`は`OsStr::as_encoded_bytes`に由来する内容のみを含む // - 空でないUTF-8の部分文字列であるASCIIの空白文字でのみ分割する unsafe { OsStr::from_encoded_bytes_unchecked(word) } }).collect();
OsStr::as_encoded_bytes
#[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub fn as_encoded_bytes(&self) -> &[u8] { /* 実装は省略 */ } }
OS文字列からバイト列のスライスに変換する。バイト列のスライスをOS文字列のスライスに戻すには、
OsStr::from_encoded_bytes_unchecked関数を使用する。
バイト列の符号化方式は未規定で、環境依存で、UTF-8の自己同期的スーパーセットである。 UTF-8の自己同期的スーパーセットであることから、この符号化方式は7ビットASCIIのスーパーセットでもある。
注意:符号化方式が未規定であるため、有効なUTF-8でないあらゆるバイト列のサブスライスはあやふやなものとして扱われるべきであり、
また同一のターゲットプラットフォーム向けにビルドされた同一のRustバージョン内でのみ比較されるべきである。
例えばネットワーク経由で送信されたスライスやファイルに保存されたスライスは非互換となる可能性が高い。
符号化方式の詳細についてはOsStringを、環境依存の指定された変換についてはstd::ffiを参照のこと。
io::Error::other
#[stable(feature = "io_error_other", since = "1.74.0")] pub fn other<E>(error: E) -> Error where E: Into<Box<dyn error::Error + Send + Sync>>, { /* 実装は省略 */ } }
任意のエラーペイロードから新しいI/Oエラーを生成する。
この関数はOS自体に起因しないI/Oエラーを汎用的に生成するために使用される。
これはError::newにErrorKind::Otherを指定するショートカットである。
サンプル
use std::io::Error; // エラーは文字列から生成できるし、 let custom_error = Error::other("oh no!"); // その他エラーからも生成できる let custom_error2 = Error::other(custom_error);
変更点リスト
言語
std::mem::Discriminant<T>がTのどんなライフタイムにも依存しない事を明文化- RFC 2145に従い、リント
private_in_publicをprivate_interfacesとprivate_boundsに置き換え。 詳細はRFC 2145を参照のこと - 明示的な
#[repr(Rust)]を許可 - クロージャにおけるフィールドの捕捉:詰め込まれたフィールドのアライメントに依存しない
asyncブロック向けにMIRに基づくドロップ追跡を有効化impl_trait_projectionsを安定化
コンパイラ
- リンク修飾子+bundleと+whole-archiveの結合を安定化
--print KIND=PATHでのPATHオプションを安定化*-apple-ios-macabiにおいてASAN/LSAN/TSANを有効化- loongarch64-unknown-none*をTier 2に格上げ
i686-pc-windows-gnullvmをTier 3ターゲットとして追加
ライブラリ
- ChildStdin/out/errが
From<OwnedFd/Handle>を実装するようになった T: Clone時にVec<T>がFrom<{&,&mut} [T; N]>を実装するようになった- impl Step for IP addresses
Rc<[T]>とArc<[T]>がFrom<[T; N]>を実装するようになったu16がTryFrom<char>を実装するようになったio_error_other機能を安定化Saturating型を安定化- const_transmute_copyを安定化
安定化されたAPI
core::num::Saturatingimpl From<io::Stdout> for std::process::Stdioimpl From<io::Stderr> for std::process::Stdioimpl From<OwnedHandle> for std::process::Child{Stdin, Stdout, Stderr}impl From<OwnedFd> for std::process::Child{Stdin, Stdout, Stderr}std::ffi::OsString::from_encoded_bytes_uncheckedstd::ffi::OsString::into_encoded_bytesstd::ffi::OsStr::from_encoded_bytes_uncheckedstd::ffi::OsStr::as_encoded_bytesstd::io::Error::otherimpl TryFrom<char> for u16impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T>impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T>impl<T, const N: usize> From<[T; N]> for Arc<[T]>impl<T, const N: usize> From<[T; N]> for Rc<[T]>
以下のAPIが定数文脈で使えるようになった。
Cargo
- fix: 内部パッケージにMSRVを設定
- config: リストを昇順で合体
- fix(update): --aggressiveの意味を--recursiveとして明確化
- fix(update):
-pを位置引数にすることで使いやすくする - feat(help): ヘルプ出力を書式化
- feat(pkgid): 曖昧でない場合に不完全なバージョンを許容
- feat: credential-processとregistry-authを安定化
- feat(cli): 予行演習(dry-run)の省略として'-n'を追加
target.'cfg(..)'.linkerへの対応を追加--keep-goingを安定化- feat: lintsテーブルを安定化
Rustdoc
- rustdocで警告ブロックへの対応を追加
- fenced code blocksにおけるユーザー定義の追加構文クラスを受容
- rustdoc-search: 型パラメータへの対応を追加
- rustdoc: 具象型の型定義に実際の列挙型と構造体を表示
互換性メモ
- Apple製OSの最小対応バージョンを引き上げ
- Cellが部分的に重なっている場合、Cell::swapがパニックするようになった
--externにおいて不正なクレート名を拒絶- 組み込みのdyn実装によって隠される可能性のある汎用実装を解決しない
- 新しい
impl From<{&,&mut} [T; N]> for Vec<T>が、一般的すぎるコードで推論失敗を引き起こすようである。tuiクレートを使う例では、AsRef<_>とInto<Vec>の組み合わせが中間型を曖昧にさせ、新しい実装が他の可能性をもたらし、 その結果明示的な型注釈が必要になってしまった
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。
この周期には無い。
関連リンク
さいごに
次のリリースのRust 1.75は12/29(金)にリリースされる予定です。
Rust 1.75では皆さん待望の、トレイトにおけるasync fn及び戻り値のimpl Traitが使えるようになる予定です。