本日1/27(金)にリリースされたRust 1.67の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
- ピックアップ
- 安定化されたAPIのドキュメント
- {unsigned integer}::ilog
- {unsigned integer}::ilog2
- {unsigned integer}::ilog10
- {unsigned integer}::checked_ilog
- {unsigned integer}::checked_ilog2
- {unsigned integer}::checked_ilog10
- {signed integer}::ilog
- {signed integer}::ilog2
- {signed integer}::ilog10
- {signed integer}::checked_ilog
- {signed integer}::checked_ilog2
- {signed integer}::checked_ilog10
- NonZeroU*::ilog2
- NonZeroU*::ilog10
- NonZero*::BITS
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
チャネルの実装が入れ替わった
標準ライブラリのチャネル(std::sync::mpsc
)には長い間放置されていたまれにパニックするというバグがあり、
このバグは根が深く5年以上も手付かずのままでした。
そこでより高速なチャネルの実装であるcrossbeam-channel
を標準ライブラリに取り込むことでこのバグに対処することになりました。
もちろん標準APIとして使用方法が変わるわけではありませんが、今回の変更によりバグ修正に加えて高速化の恩恵もあるようです。
ところで、標準ライブラリがMPSC(複数入力単一出力)なのに対してcrossbeam-channel
はMPMC(複数入力複数出力)です。
実はcrossbeam-channel
を標準ライブラリに取り込むにあたって非公開ではあるもののstd::sync::mpmc
というモジュールが生えており、
std::sync::mpsc
はそのラッパーという形になっています。
取り込みのPRではこれを公開する可能性に言及しているので将来的にはチャネルの機能が拡張されるかもしれません。
非同期関数の#[must_use]が戻り値にも適用されるようになった
これまでは非同期関数(async fn
)に#[must_use]
を付けても.await
したあとの値ではなく(直接の戻り値である)Future
のみに影響していました。
Rust 1.67からは関数の戻り値(.awaitしたあとの値
)にも#[must_use]
が適用されるようになり、よりミスをする可能性が減ります。
#[must_use] async fn hoge() -> u32 { 42 } async fn fuga() { // Rust 1.66まではここでは警告が出ないが1.67からは出る hoge().await; // ^^^^^^^^^^^^^ // unused output of future returned by `hoge` that must be used }
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
{unsigned integer}::ilog
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog(self, base: Self) -> u32 { /* 実装は省略 */ } }
任意の数値を底とした対数を切り捨てて返す。
このメソッドが最適化されるかは実装依存である。
ilog2
は底2に対してより効率的に結果を生成でき、またilog10
は底10に対してより効率的に結果を生成できる。
パニック
このメソッドはself
がゼロ、あるいはbase
が2未満の場合にパニックを起こす。
サンプル
assert_eq!(5u8.ilog(5), 1);
{unsigned integer}::ilog2
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog2(self) -> u32 { /* 実装は省略 */ } }
2を底とした対数を切り捨てて返す。
パニック
この関数はself
がゼロの場合にパニックを起こす。
サンプル
assert_eq!(2u8.ilog2(), 1);
{unsigned integer}::ilog10
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog10(self) -> u32 { /* 実装は省略 */ } }
10を底とした対数を切り捨てて返す。
パニック
この関数はself
がゼロの場合にパニックを起こす。
サンプル
assert_eq!(10u8.ilog10(), 1);
{unsigned integer}::checked_ilog
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog(self, base: Self) -> Option<u32> { /* 実装は省略 */ } }
任意の数値を底とした対数を切り捨てて返す。
対数を取る数値がゼロの場合がゼロの場合、あるいは底が2未満の場合にNone
を返す。
このメソッドが最適化されるかは実装依存である。
checked_ilog2
は底2に対してより効率的に結果を生成でき、またchecked_ilog10
は底10に対してより効率的に結果を生成できる。
サンプル
assert_eq!(5u8.checked_ilog(5), Some(1));
{unsigned integer}::checked_ilog2
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog2(self) -> Option<u32> { /* 実装は省略 */ } }
2を底とした対数を切り捨てて返す。
数値がゼロの場合はNone
を返す。
サンプル
assert_eq!(2u8.checked_ilog2(), Some(1));
{unsigned integer}::checked_ilog10
impl u8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option<u32> { /* 実装は省略 */ } }
10を底とした対数を切り捨てて返す。
数値がゼロの場合はNone
を返す。
サンプル
assert_eq!(10u8.checked_ilog10(), Some(1));
{signed integer}::ilog
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog(self, base: Self) -> u32 { /* 実装は省略 */ } }
任意の数値を底とした対数を切り捨てて返す。
このメソッドが最適化されるかは実装依存である。
ilog2
は底2に対してより効率的に結果を生成でき、またilog10
は底10に対してより効率的に結果を生成できる。
パニック
このメソッドはself
がゼロ以下、あるいはbase
が2未満の場合にパニックを起こす。
サンプル
assert_eq!(5i8.ilog(5), 1);
{signed integer}::ilog2
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog2(self) -> u32 { /* 実装は省略 */ } }
2を底とした対数を切り捨てて返す。
パニック
この関数はself
がゼロ以下の場合にパニックを起こす。
サンプル
assert_eq!(2i8.ilog2(), 1);
{signed integer}::ilog10
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog10(self) -> u32 { /* 実装は省略 */ } }
10を底とした対数を切り捨てて返す。
パニック
この関数はself
がゼロ以下の場合にパニックを起こす。
サンプル
assert_eq!(10i8.ilog10(), 1);
{signed integer}::checked_ilog
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog(self, base: Self) -> Option<u32> { /* 実装は省略 */ } }
任意の数値を底とした対数を切り捨てて返す。
対数を取る数値がゼロの場合が負かゼロの場合、あるいは底が2未満の場合にNone
を返す。
このメソッドが最適化されるかは実装依存である。
checked_ilog2
は底2に対してより効率的に結果を生成でき、またchecked_ilog10
は底10に対してより効率的に結果を生成できる。
サンプル
assert_eq!(5i8.checked_ilog(5), Some(1));
{signed integer}::checked_ilog2
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog2(self) -> Option<u32> { /* 実装は省略 */ } }
2を底とした対数を切り捨てて返す。
数値が負かゼロの場合はNone
を返す。
サンプル
assert_eq!(2i8.checked_ilog2(), Some(1));
{signed integer}::checked_ilog10
impl i8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option<u32> { /* 実装は省略 */ } }
10を底とした対数を切り捨てて返す。
数値が負かゼロの場合はNone
を返す。
サンプル
assert_eq!(10i8.checked_ilog10(), Some(1));
NonZeroU*::ilog2
impl NonZeroU8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn ilog2(self) -> u32 { /* 実装は省略 */ } }
2を底とした対数を切り捨てて返す。
これは、値がゼロではないために関数が失敗する心配がないこと以外はu8::ilog2
と同じ操作である。
サンプル
assert_eq!(NonZeroU8::new(7).unwrap().ilog2(), 2); assert_eq!(NonZeroU8::new(8).unwrap().ilog2(), 3); assert_eq!(NonZeroU8::new(9).unwrap().ilog2(), 3);
NonZeroU*::ilog10
impl NonZeroU8 { #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn ilog10(self) -> u32 { /* 実装は省略 */ } }
10を底とした対数を切り捨てて返す。
これは、値がゼロではないために関数が失敗する心配がないこと以外はi8::ilog2
と同じ操作である。
サンプル
assert_eq!(NonZeroU8::new(99).unwrap().ilog10(), 1); assert_eq!(NonZeroU8::new(100).unwrap().ilog10(), 2); assert_eq!(NonZeroU8::new(101).unwrap().ilog10(), 2);
NonZero*::BITS
impl NonZeroU8 { #[stable(feature = "nonzero_bits", since = "1.67.0")] pub const BITS: u32 = <u8>::BITS; }
非ゼロな数値型におけるビット数。
この値はu8::BITS
と同じである。
サンプル
assert_eq!(NonZeroU8::BITS, u8::BITS);
変更点リスト
言語
Sized
述語を余帰納的(coinductive)にし再帰を許可async fn
での#[must_use]
注釈がFuture::Output
にも作用するようになった- クロージャのシグネチャ(signature、引数等の構成)を推論する際に親トレイトの責務を推敲する
cfg(FALSE)
の中での不正なリテラルがエラーにならなくなった- 値の名前空間上における中括弧付き列挙型のバリアントを予約解除。
※訳注:列挙型での構造体風のバリアントをuse
などで型名無しで使えるようにしている際、 バリアントと同名の変数名を付けられるようになった、ということらしい
コンパイラ
C
やcdecl
以外の呼び出し規約において可変長引数への対応を有効化。
※訳注:win64
やsysv64
、elfapi
で可変長引数が使えるようになったと言うことであり、 Rust標準の呼び出し規約では使えないままな上これらにおいてもNightly限定- データフロー解析に基づく新しいMIRの定数性伝播を追加
- m*2nのサイズのフィールドを等しくアライメントされたフィールドでグループ化することによりフィールドの順序を最適化
- ネイティブライブラリ向けの修飾子
verbatim
を安定化。
※訳注:#[link(name = "xxx", modifiers = "+verbatim")]
とするとname
にターゲット固有の接頭辞や接尾辞が追加されず、 参照するファイル名にlibxxx.a
やxxx.dll
ではなくxxx
が使われる
追加または削除されたターゲット
- AIX上のPowerPCをTier 3のターゲットとして追加:
powerpc64-ibm-aix
- 初代プレイステーションをTier 3のターゲットとして追加:
mipsel-sony-psx
- QNX Neutrino RTOSをTier 3の
no_std
なターゲットとして追加:aarch64-unknown-nto-qnx710
とx86_64-pc-nto-qnx710
- Tier 3のターゲットである
linuxkernel
を削除(実際のカーネルには非使用)
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
crossbeam-channel
をstd::sync::mpsc
にマージ- 小数第0位でフォーマットする際に0.5だけ丸めに一貫性がないのを修正。
※訳注:基本的には偶数丸め(銀行丸め)であったが0.5
と-0.5
で一貫性がなかった模様 ControlFlow
においてEq
とHash
を自動導出compiler_builtins
を-C panic=abort
でビルドしない
安定化されたAPI
{integer}::checked_ilog
{integer}::checked_ilog2
{integer}::checked_ilog10
{integer}::ilog
{integer}::ilog2
{integer}::ilog10
NonZeroU*::ilog2
NonZeroU*::ilog10
NonZero*::BITS
以下のAPIが定数文脈で使えるようになった。
互換性メモ
repr(Rust)
な型のレイアウトがm*2nのサイズのフィールドを等しくアライメントされたフィールドで グループ化されるようになった。 これは最適化のためであるが、一部の事例で列挙型でのタグ配置のために型の大きさが膨れる場合がある。 なおrepr(Rust)
な型のレイアウトは実装依存であり、変更される可能性があることに注意されたい- 小数第0位でフォーマットフォーマットする際に0.5が0に丸められるようになった。 これにより、偶数に丸める他の浮動小数点数のフォーマットと動作が一致するようになった
&&
と||
の連鎖において、内側の式からの一時変数が評価順(左から右)にドロップするようになった。 これまでは他の式が順番に一時変数をドロップしたあと、最初の式が最後にドロップするように「ねじれた」状態だった。
※訳注:let_chains(if
の中で複数の条件とlet
を混ぜられるやつ)の安定に向けたバグ修正。 とは言え依然として問題は山積しているので安定化にはしばらく掛かりそうな予感)- 文字列リテラルの最後に付くアンダーバーがコンパイルエラーになった。 これは1.20.0から将来に向けた互換性の警告だった
wasm-ld
に-export-dynamic
を渡さないようになったwasm32-wasi
においてmain
が__main_void
として修飾(mangle)されるようになった- Cargoにおいて、設定内に同じインデックスURLのレジストリが複数ある場合にエラーを出力するようになった
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。
- LLVMのアーカイブを書き込む部分をRustで書き直した。
※訳注:LLVM以外のコード生成バックエンドを見据えた変更っぽい
関連リンク
さいごに
次のリリースのRust 1.68は3/10(金)にリリースされる予定です。
Rust 1.68ではFutureなどをピン留めするのに便利なpin!
マクロが使えるようになる予定です。