こんにちは、あずんひ(@aznhe21)です。8月25日に発売された地球防衛軍6がシリーズ中最も濃密なストーリーで非常に楽しめたのでおすすめです。
さて、本日9/23(金)にRust 1.64がリリースされました。 この記事ではRust 1.64での変更点を詳しく紹介します。 よろしければ記事末尾から活動支援頂けると嬉しいです。
なお、Rust 1.64の目玉(?)であったlet-chains(if式でのletによる束縛が複数回使える)はバグのため安定化が見送られました。
- ピックアップ
- 安定化されたAPIのドキュメント
- core::future::IntoFuture
- core::future::poll_fn
- core::future::PollFn
- core::task::ready!
- core::num::NonZero*::checked_mul
- core::num::NonZero*::saturating_mul
- core::num::NonZero*::checked_pow
- core::num::NonZero*::saturating_pow
- core::num::NonZeroI*::abs
- core::num::NonZeroI*::checked_abs
- core::num::NonZeroI*::overflowing_abs
- core::num::NonZeroI*::saturating_abs
- core::num::NonZeroI*::wrapping_abs
- core::num::NonZeroI*::unsigned_abs
- core::num::NonZeroU*::checked_add
- core::num::NonZeroU*::saturating_add
- core::num::NonZeroU*::checked_next_power_of_two
- std::os::unix::process::CommandExt::process_group
- std::os::windows::fs::FileTypeExt
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
libstdにあるFFI系の型がlibcore・liballocに移動した
多くのFFI系の型がlibstdだけではなくlibcore・liballocでも使えるようになりました。 これにより、組み込み系の環境でもCとの連携がやりやすくなります。
CStr
及び関連する型がlibcoreで、
またCString
及び関連する型がliballocに移動しました。
また、次の型がstd::os::raw
だけでなくcore::ffi
・std::ffi
で使えるようになりました。
c_char
c_double
c_float
c_int
c_long
c_longlong
c_schar
c_short
c_uchar
c_uint
c_ulong
c_ulonglong
c_ushort
.await時にIntoFutureが使われるようになった
これまでxxx.await
におけるxxx
にはFuture
トレイトを実装する型を受け入れていました。
Rust 1.64から、xxx
にはFuture
の代わりにIntoFuture
を受け入れるようになります。
これは「非同期ビルダー」で真価を発揮します。
例えばreqwestクレートやsurfクレートではリクエストを生成するRequestBuilder
からリクエストを送信する場合に.send()
という文が必要でしたが、
RequestBuilder
にIntoFuture
を実装すればこれが不要になります。
async fn main() { let url = "https://example.com"; let client = reqwest::Client::new(); // Rust 1.63以前 let resp = client.get(url).header("Hoge", "Fuga").send().await.unwrap(); let resp = surf::get(url).header("Hoge", "Fuga").send().await.unwrap(); // Rust 1.64以降はこう書けるようになるかも? let resp = client.get(url).header("Hoge", "Fuga").await.unwrap(); let resp = surf::get(url).header("Hoge", "Fuga").await.unwrap(); }
これはfor
文でループ対象の値にIntoIterator
によって値が変換されるよう脱糖されるのと同じように、
値を.await
した際はIntoFuture
によって値が変換されるよう脱糖されます。
// このループは・・・ for x in a {} // こう脱糖される let mut _iter = a.into_iter(); while let Some(x) = _iter.next() {} // この待機は・・・ let y = b.await; // 雰囲気的にはこう脱糖される let mut _fut = b.into_future(); let y = loop { match _fut.poll(&mut cx) { Poll::Pending => {}, Poll::Ready(x) => break x, } };
Cargoでワークスペース共通の設定を持てるようになった
Cargoでワークスペースを使用しているとき、ワークスペース側のCargo.toml
にバージョンや概要などのパッケージ情報、
さらには依存クレートも記述出来るようになりました。
パッケージ情報をworkspace.package
に、依存クレートをworkspace.dependencies
に書くことで、
ワークスペース内の全クレートに継承させることができます。ただしサブクレート側での指定が必要なことに注意が必要です。
# Cargo.toml # ワークスペースの定義 [workspace] members = ["sub"] # ワークスペース共通の設定 [workspace.package] version = "1.2.3" # ワークスペース共通のバージョン edition = "2021" # ワークスペース共通のRustエディション rust-version = "1.64" # ワークスペース共通のMSRV license = "GPL-3.0-or-later" # ワークスペース共通のライセンス description = "色んなことをやるクレート" # ワークスペース共通の概要 # ワークスペース共通の依存クレート [workspace.dependencies] tokio = { version = "1", features = ["full"] }
# sub/Cargo.toml [package] name = "sub" # ワークスペース共通の設定を使うには以下のような指定が必要 version.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true description.workspace = true [dependencies] # ワークスペース共通のクレート師弟を使うには以下のような指定が必要 tokio.workspace = true
workspace.package
には以下のフィールドを指定できます。
license-file
・readme
はワークスペースのルートからの相対パスとして解釈されます。
また、include
・exclude
はサブクレートからの相対パスとして解釈されます。
authors
categories
description
documentation
edition
exclude
homepage
include
keywords
license
license-file
publish
readme
repository
rust-version
version
mem::uninitializedが安全のために遅くなった
mem::uninitialized
はその名の通り未初期化のメモリ領域を返す関数でしたが、
Rust 1.64からは対象のメモリ領域全体に0x01が書き込まれるようになったため、かなりの低速化が見込まれます
(Visual C++でデバッグ窓にフフフフフ(=0xCC)と出るのと似ています)。
現在はmem::uninitialized
の代わりにMaybeUninit
を使用することが強く推奨されているため、まだ移行していない方は移行しておくと良いでしょう。
0x01が書き込まれるようになった理由ですが、mem::uninitialized
が誤って使われることが多いためで、
特に型がbool
だと値が0と1のどちらでもない場合は未定義動作であるため問題は深刻でした。
そのため値に0x01を書き込むことで、誤った使われ方でも未定義動作が発生しないよう緩和策が入ったのです。
なお、既にmem::uninitialized
の間違った使い方では実行時にパニックが起きますし、
そもそもmem::uninitialized
を使用できなくするという議論も進んでいます。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
core::future::IntoFuture
#[stable(feature = "into_future", since = "1.64.0")] pub trait IntoFuture { #[stable(feature = "into_future", since = "1.64.0")] type Output; #[stable(feature = "into_future", since = "1.64.0")] type IntoFuture: Future<Output = Self::Output>; #[stable(feature = "into_future", since = "1.64.0")] #[lang = "into_future"] fn into_future(self) -> Self::IntoFuture; }
Future
への変換。
型に対してIntoFuture
を実装することにより、その型がFuture
へどう変換されるかが定義される。
.await
の脱糖
.await
キーワードは、Futureをポーリングして完了する前に、まずIntoFuture::into_future
を呼び出すよう脱糖する。
IntoFuture
はすべてのT: Future
を満たす型に実装されるため、into_future
メソッドはすべてのFutureにおいて利用可能である。
use std::future::IntoFuture; let v = async { "ニャー" }; let mut fut = v.into_future(); assert_eq!("ニャー", fut.await);
非同期ビルダー
Futureを手動で実装する場合、大抵はFuture
とIntoFuture
のどちらを型に実装するかを選択することになる。
ほとんどの場合にはFuture
を実装する方が良い選択肢ではあるものの、
「非同期ビルダー」型を実装する際にはIntoFuture
を実装する方が有用である。
こう言った値では、.await
される前に値を複数回変更することができる。
use std::future::{ready, Ready, IntoFuture}; /// のちのちに2つの数値を乗ずる pub struct Multiply { num: u16, factor: u16, } impl Multiply { /// `Multiply`の新しいインスタンスを構築する。 pub fn new(num: u16, factor: u16) -> Self { Self { num, factor } } /// 被乗数を設定する。 pub fn number(mut self, num: u16) -> Self { self.num = num; self } /// 乗数を設定する。 pub fn factor(mut self, factor: u16) -> Self { self.factor = factor; self } } impl IntoFuture for Multiply { type Output = u16; type IntoFuture = Ready<Self::Output>; fn into_future(self) -> Self::IntoFuture { ready(self.num * self.factor) } } // NOTE: Rustではまだ`async fn main`関数は使えず、今のところそういった機能はエコシステムにしか存在しない async fn run() { let num = Multiply::new(0, 0) // ビルダーを次のように初期化する。number: 0, factor: 0 .number(2) // 被乗数を2に設定する .factor(2) // 乗数を2に設定する .await; // Futureに変換して.awaitする assert_eq!(num, 4); }
トレイト境界での使い方
IntoFuture
をトレイト境界に使うことで、関数をFuture
とIntoFuture
に対して汎化することができる。
これは関数の利用者にとっては便利であり、Future
を得るためにIntoFuture::into_future
を無駄に呼び出す必要が無くなる。
use std::future::IntoFuture; /// Futureの戻り値を文字列に変換する。 async fn fut_to_string<Fut>(fut: Fut) -> String where Fut: IntoFuture, Fut::Output: std::fmt::Debug, { format!("{:?}", fut.await) }
必須の関連型
type Output
Futureが完了した時に生成する戻り値。
type IntoFuture: Future where <Self::IntoFuture as Future>::Output == Self::Output
変換後のFutureの型。
必須のメソッド
fn into_future(self) -> Self::IntoFuture
値からFutureを生成する。
サンプル
基本的な使い方。
use std::future::IntoFuture; let v = async { "ニャー" }; let mut fut = v.into_future(); assert_eq!("ニャー", fut.await);
use std::future::IntoFuture; async fn foo() { let v = async { "meow" }; let mut fut = v.into_future(); assert_eq!("meow", fut.await); }
core::future::poll_fn
#[stable(feature = "future_poll_fn", since = "1.64.0")] pub fn poll_fn<T, F>(f: F) -> PollFn<F> where F: FnMut(&mut Context<'_>) -> Poll<T>, { /* 実装は省略 */ } }
Poll
を返す関数をラップしたFutureを生成する。
Futureをポーリングするとラップされた関数に委譲される。
サンプル
use core::future::poll_fn; use std::task::{Context, Poll}; fn read_line(_cx: &mut Context<'_>) -> Poll<String> { Poll::Ready("Hello, World!".into()) } let read_future = poll_fn(read_line); assert_eq!(read_future.await, "Hello, World!".to_owned());
async fn run() { use core::future::poll_fn; use std::task::{Context, Poll}; fn read_line(_cx: &mut Context<'_>) -> Poll<String> { Poll::Ready("Hello, World!".into()) } let read_future = poll_fn(read_line); assert_eq!(read_future.await, "Hello, World!".to_owned()); }
core::future::PollFn
#[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "future_poll_fn", since = "1.64.0")] pub struct PollFn<F> { /* フィールドは省略 */ }
Poll
を返す関数をラップしたFuture。
この構造体はpoll_fn
によって生成される。詳細はそちらの文書を参照されたい。
core::task::ready!
#[stable(feature = "ready_macro", since = "1.64.0")] #[rustc_macro_transparency = "semitransparent"] pub macro ready($e:expr) { /* 定義は省略 */ }
Poll<T>
の成功時の型を抽出する。
このマクロは早期リターンにより、Pending
信号の伝播を停止する。
サンプル
use std::task::{ready, Context, Poll}; use std::future::{self, Future}; use std::pin::Pin; pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { let mut fut = future::ready(42); let fut = Pin::new(&mut fut); let num = ready!(fut.poll(cx)); // ... numを使う Poll::Ready(()) }
ready!
呼び出しは次の様に展開される。
let num = match fut.poll(cx) { Poll::Ready(t) => t, Poll::Pending => return Poll::Pending, };
core::num::NonZero*::checked_mul
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_mul(self, other: NonZeroUsize) -> Option<NonZeroUsize> { /* 実装は省略 */ } }
2つの非ゼロ整数を乗算する。
オーバーフローをチェックし、オーバーフロー時にはNone
を返す。
結果として戻り値がゼロに折り返されることはない。
サンプル
let two = NonZeroUsize::new(2)?; let four = NonZeroUsize::new(4)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(Some(four), two.checked_mul(two)); assert_eq!(None, max.checked_mul(two));
core::num::NonZero*::saturating_mul
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn saturating_mul(self, other: NonZeroUsize) -> NonZeroUsize { /* 実装は省略 */ } }
2つの非ゼロ整数を乗算する。
オーバーフロー時にはusize::MAX
(※訳注:使用する型による。型がNonZeroU8
であればu8::MAX
)を返す。
サンプル
let two = NonZeroUsize::new(2)?; let four = NonZeroUsize::new(4)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(four, two.saturating_mul(two)); assert_eq!(max, four.saturating_mul(max));
core::num::NonZero*::checked_pow
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_pow(self, other: u32) -> Option<NonZeroUsize> { /* 実装は省略 */ } }
非ゼロの値を整数のべき乗に引き上げる。
オーバーフローをチェックし、オーバーフロー時にはNone
を返す。
結果として戻り値がゼロに折り返されることはない。
サンプル
let three = NonZeroUsize::new(3)?; let twenty_seven = NonZeroUsize::new(27)?; let half_max = NonZeroUsize::new(usize::MAX / 2)?; assert_eq!(Some(twenty_seven), three.checked_pow(3)); assert_eq!(None, half_max.checked_pow(3));
core::num::NonZero*::saturating_pow
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn saturating_pow(self, other: u32) -> NonZeroUsize { /* 実装は省略 */ } }
非ゼロの値を整数のべき乗に引き上げる。
オーバーフロー時にはusize::MAX
(※訳注:使用する型による。型がNonZeroU8
であればu8::MAX
)を返す。
サンプル
let three = NonZeroUsize::new(3)?; let twenty_seven = NonZeroUsize::new(27)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(twenty_seven, three.saturating_pow(3)); assert_eq!(max, max.saturating_pow(3));
core::num::NonZeroI*::abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn abs(self) -> NonZeroIsize { /* 実装は省略 */ } }
自身の絶対値を計算する。オーバーフロー時の挙動についてはisize::abs
(※訳注:使用する型による。型がNonZeroI8
であればi8::abs
)を参照されたい。
サンプル
let pos = NonZeroIsize::new(1)?; let neg = NonZeroIsize::new(-1)?; assert_eq!(pos, pos.abs()); assert_eq!(pos, neg.abs());
core::num::NonZeroI*::checked_abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_abs(self) -> Option<NonZeroIsize> { /* 実装は省略 */ } }
チェック済みの絶対値。
オーバーフローをチェックし、もしself == isize::MIN
(※訳注:使用する型による。型がNonZeroI8
であればself == i8::MIN
)の場合はNone
を返す。
戻り値がゼロになることはない。
サンプル
let pos = NonZeroIsize::new(1)?; let neg = NonZeroIsize::new(-1)?; let min = NonZeroIsize::new(isize::MIN)?; assert_eq!(Some(pos), neg.checked_abs()); assert_eq!(None, min.checked_abs());
core::num::NonZeroI*::overflowing_abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn overflowing_abs(self) -> (NonZeroIsize, bool) { /* 実装は省略 */ } }
自身の絶対値を計算する。オーバーフローについてはisize::overflowing_abs
(※訳注:使用する型による。型がNonZeroI8
であればi8::overflowing_abs
)を参照されたい。
サンプル
let pos = NonZeroIsize::new(1)?; let neg = NonZeroIsize::new(-1)?; let min = NonZeroIsize::new(isize::MIN)?; assert_eq!((pos, false), pos.overflowing_abs()); assert_eq!((pos, false), neg.overflowing_abs()); assert_eq!((min, true), min.overflowing_abs());
core::num::NonZeroI*::saturating_abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn saturating_abs(self) -> NonZeroIsize { /* 実装は省略 */ } }
飽和した絶対値。isize::saturating_abs
(※訳注:使用する型による。型がNonZeroI8
であればi8::saturating_abs
)を参照。
サンプル
let pos = NonZeroIsize::new(1)?; let neg = NonZeroIsize::new(-1)?; let min = NonZeroIsize::new(isize::MIN)?; let min_plus = NonZeroIsize::new(isize::MIN + 1)?; let max = NonZeroIsize::new(isize::MAX)?; assert_eq!(pos, pos.saturating_abs()); assert_eq!(pos, neg.saturating_abs()); assert_eq!(max, min.saturating_abs()); assert_eq!(max, min_plus.saturating_abs());
core::num::NonZeroI*::wrapping_abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn wrapping_abs(self) -> NonZeroIsize { /* 実装は省略 */ } }
折り返された絶対値。isize::wrapping_abs
(※訳注:使用する型による。型がNonZeroI8
であれば[ i8::wrapping_abs
])を参照。
サンプル
let pos = NonZeroIsize::new(1)?; let neg = NonZeroIsize::new(-1)?; let min = NonZeroIsize::new(isize::MIN)?; let max = NonZeroIsize::new(isize::MAX)?; assert_eq!(pos, pos.wrapping_abs()); assert_eq!(pos, neg.wrapping_abs()); assert_eq!(min, min.wrapping_abs());
core::num::NonZeroI*::unsigned_abs
impl NonZeroIsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn unsigned_abs(self) -> NonZeroUsize { /* 実装は省略 */ } }
折り返しもパニックもなしに絶対値を計算する。
サンプル
let u_pos = NonZeroUsize::new(1)?; let i_pos = NonZeroIsize::new(1)?; let i_neg = NonZeroIsize::new(-1)?; let i_min = NonZeroIsize::new(isize::MIN)?; let u_max = NonZeroUsize::new(usize::MAX / 2 + 1)?; assert_eq!(u_pos, i_pos.unsigned_abs()); assert_eq!(u_pos, i_neg.unsigned_abs()); assert_eq!(u_max, i_min.unsigned_abs());
core::num::NonZeroU*::checked_add
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_add(self, other: usize) -> Option<NonZeroUsize> { /* 実装は省略 */ } }
符号無し整数を非ゼロの値に足す。オーバーフローをチェックし、オーバーフロー時にはNone
を返す。
結果として戻り値がゼロに折り返されることはない。
サンプル
let one = NonZeroUsize::new(1)?; let two = NonZeroUsize::new(2)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(Some(two), one.checked_add(1)); assert_eq!(None, max.checked_add(1));
core::num::NonZeroU*::saturating_add
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn saturating_add(self, other: usize) -> NonZeroUsize { /* 実装は省略 */ } }
符号無し整数を非ゼロの値に足す。オーバーフロー時にはusize::MAX
(※訳注:使用する型による。型がNonZeroU8
であればu8::MAX
)を返す。
サンプル
let one = NonZeroUsize::new(1)?; let two = NonZeroUsize::new(2)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(two, one.saturating_add(1)); assert_eq!(max, max.saturating_add(1));
core::num::NonZeroU*::checked_next_power_of_two
impl NonZeroUsize { #[stable(feature = "nonzero_checked_ops", since = "1.64.0")] #[rustc_const_stable(feature = "const_nonzero_checked_ops", since = "1.64.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_next_power_of_two(self) -> Option<NonZeroUsize> { /* 実装は省略 */ } }
n以上の、最小の2の累乗を返す。
オーバーフローをチェックし、次の2の累乗が型の最大値より大きい場合にはNone
を返す。
結果として戻り値がゼロに折り返されることはない。
サンプル
let two = NonZeroUsize::new(2)?; let three = NonZeroUsize::new(3)?; let four = NonZeroUsize::new(4)?; let max = NonZeroUsize::new(usize::MAX)?; assert_eq!(Some(two), two.checked_next_power_of_two() ); assert_eq!(Some(four), three.checked_next_power_of_two() ); assert_eq!(None, max.checked_next_power_of_two() );
std::os::unix::process::CommandExt::process_group
#[stable(feature = "rust1", since = "1.0.0")] pub trait CommandExt: Sealed { #[stable(feature = "process_set_process_group", since = "1.64.0")] fn process_group(&mut self, pgroup: i32) -> &mut process::Command { /* 実装は省略 */ } }
子プロセスのプロセスグループID(PGID)を設定する。子プロセスからsetpgid
を呼び出すのと同義だが、より効率的な可能性がある。
プロセスグループはシグナルを受け取るプロセスを決定する。
サンプル
ターミナルでCtrl-Cを押すと、現在前面のプロセスグループにあるすべてのプロセスにSIGINTが送られる。
sleep
サブプロセスを新しいプロセスグループで起動することで、そのプロセスはターミナルからSIGINTを受け取らなくなる。
親プロセスはシグナルハンドラを注入してサブプロセスを独自に管理することができる。
プロセスグループのIDが0の場合、プロセスIDがPGIDとして扱われる。
use std::process::Command; use std::os::unix::process::CommandExt; Command::new("sleep") .arg("10") .process_group(0) .spawn()? .wait()?;
std::os::windows::fs::FileTypeExt
#[stable(feature = "windows_file_type_ext", since = "1.64.0")] pub trait FileTypeExt: Sealed { #[stable(feature = "windows_file_type_ext", since = "1.64.0")] fn is_symlink_dir(&self) -> bool; #[stable(feature = "windows_file_type_ext", since = "1.64.0")] fn is_symlink_file(&self) -> bool; }
fs::FileType
への、Windows限定の拡張。
Windowsでは、シンボリックリンクそれ自体がファイルかディレクトリであるかを知っている。
必須のメソッド
fn is_symlink_dir(&self) -> bool
このFileType
がシンボリックリンクであり、かつディレクトリである場合にtrue
を返す。
fn is_symlink_file(&self) -> bool
このFileType
がシンボリックリンクであり、かつファイルである場合にtrue
を返す。
変更点リスト
言語
- 共用体において、可変参照や許可された型のタプルが許可されるようになった
T
内のすべてのバイトがUnsafeCell
内にある場合、共有参照&T
が指すメモリを解放することは有効と見做されるようになった- タプル構造体の未使用フィールドは構造体の未使用フィールドと同様、
unused_tuple_struct_fields
という初期許可のリントとして警告されるようになった。 このリントは将来的に初期警告となる予定である
コンパイラ
- Nintendo Switchがティア3ターゲットとして追加された
- Rustのティア付けされたプラットフォームサポートの詳細はPlatform Supportのページ(※訳注:英語)を参照
- ELFターゲットでは、
#[used]
はllvm.compiler.used
としてコンパイルされるだけになった - コンパイラにフラグ
--diagnostic-width
を追加し、ターミナルの幅を定義できるようになった - iOS、tvOS、watchOSにおいて、
rust-lld
のlink-flavorをサポートするようになった
ライブラリ
- 比較交換(compare-exchange)におけるメモリ順序の制限を削除
OsString
に対してwrite!
やwriteln!
を使えるようになった:Implementfmt::Write
forOsString
RwLockReadGuard
を共変化std::net::[Into]Incoming
がFusedIterator
を実装するようになったimpl<T: AsRawFd> AsRawFd for {Arc,Box}<T>
ptr::copy
とptr::swap
が型なしでコピーするようになったavailable_parallelism
にcgroupv1へのサポートを追加mem::uninitialized
における多くの誤った使用を緩和
安定化されたAPI
future::IntoFuture
future::poll_fn
task::ready!
num::NonZero*::checked_mul
num::NonZero*::checked_pow
num::NonZero*::saturating_mul
num::NonZero*::saturating_pow
num::NonZeroI*::abs
num::NonZeroI*::checked_abs
num::NonZeroI*::overflowing_abs
num::NonZeroI*::saturating_abs
num::NonZeroI*::unsigned_abs
num::NonZeroI*::wrapping_abs
num::NonZeroU*::checked_add
num::NonZeroU*::checked_next_power_of_two
num::NonZeroU*::saturating_add
os::unix::process::CommandExt::process_group
os::windows::fs::FileTypeExt::is_symlink_dir
os::windows::fs::FileTypeExt::is_symlink_file
これらの型はstd::ffi
で安定化されていたが、core
とalloc
でも使えるようになった。
core::ffi::CStr
core::ffi::FromBytesWithNulError
alloc::ffi::CString
alloc::ffi::FromVecWithNulError
alloc::ffi::IntoStringError
alloc::ffi::NulError
これらの型はstd::os::raw
で安定化されていたが、core::ffi
とstd::ffi
でも使えるようになった。
ffi::c_char
ffi::c_double
ffi::c_float
ffi::c_int
ffi::c_long
ffi::c_longlong
ffi::c_schar
ffi::c_short
ffi::c_uchar
ffi::c_uint
ffi::c_ulong
ffi::c_ulonglong
ffi::c_ushort
以下のAPIが定数文脈で使えるようになった。
Cargo
- パッケージがワークスペースから設定を継承することにより、設定を一カ所に集中できるようになった。
こういった共通設定を定義する方法は
workspace.package
やworkspace.dependencies
を参照されたい - cargoのコマンドが複数の
--target
フラグを受け付けるようになり、一度に複数のターゲットへビルドできるようになり、 また構成オプションbuild.target
が複数のターゲットを取れるようになった --jobs
引数が負数を取れるようになり、最大のCPU数から逆算できるようになったcargo add
がCargo.lock
を更新するようになったcargo rustc
に--crate-type
フラグを追加し、クレートの種類を優先させられるようになった- GitHubからgitの依存関係を取ってくる際、
rev
フィールドでハッシュを用いる場合のパフォーマンスを大幅に改善した
その他
互換性メモ
- すべての
-linux-gnu
ターゲットにおいて、以前は古いバージョンをサポートしていたターゲットでも 最低限必要なバージョンがカーネル3.2、glibc 2.17となった:Increase the minimum linux-gnu versions - ネットワークの基本型が(Cのシステムレイアウトではなく)Rustの理想的なレイアウトで実装されるようになった。
これらの型を
transmute
している場合は問題が発生する transmute_copy
にUがTより大きくないことのassertを追加BTreeMap
の不良動作(unsoundness)が修正された。借用中のデータがコンテナより先にドロップすることを許容してしまっていた- C的なenumから整数にキャストする場合のDropの挙動が変更された。これは既にコンパイラの警告によって阻止されている
- NLLでは遅延バインドされるクロージャのライフタイムを親の関数に関連付け
- 定数評価時のエラーが将来的な非互換報告に含まれるようになった
thumbv6m-none-eabi
ターゲットでいくつかの不正なasm!
文が高位のレジスタ(r8からr14)が入出力オペランドとして使われている場合に誤って容認されていた。 これが容認されないようになった- 隠れた型が関連型のすべてのトレイト境界を満たしている限り、
impl Trait
がその境界を満たさなくても、戻り値に位置するimpl Trait
の関連型の値として誤って容認されていた。これが修正された
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。
- Windows版ビルドでプロファイルに基づく最適化が用いられるようになり、コンパイラのパフォーマンスが10~20%改善された:Utilize PGO for windows x64 rustc dist builds
- ディスクに書き込む前にメタデータをメモリに保持するのをやめる
- compiletest:mode=uiではデバッグ情報を既定で排除
#[derive]
での生成コードへの多数の改善(パフォーマンス改善を含む)- clap 3にバージョンアップ
- ドロップチェッカーをmirに完全移行
index == len
の場合向けにVec::insert
を最適化- rust-analyzerをツリー内のツールへ転換
関連リンク
さいごに
次のリリースのRust 1.65は11/4(金)にリリースされる予定です。 1.65では次のように様々な機能が使えるようになる予定です。
std::backtrace
によるバックトレースの管理- 名前付きブロックからの値付きbreak:
let x = 'a: { if true { break 'a 0; } 1 };
- 関連型でのジェネリクス(GATs):
trait X { type Y<T> where T: Trait; }
- パターンにマッチしなかった場合の
else
(Swiftのguard的なやつ):let Ok(_) = x else { return }
ライセンス表記
- この記事はApache 2/MITのデュアルライセンスで公開されている公式リリースノート及びドキュメントから翻訳・追記をしています
- 冒頭の画像中にはRust公式サイトで配布されているロゴを使用しており、 このロゴはRust財団によってCC-BYの下で配布されています
- 冒頭の画像中にはFirefox公式サイトで配布されているロゴを使用しており、 このロゴはMozilla FoundationによってCC-BY-SA 3.0の下で配布されています
- 冒頭の画像中にはAndroid公式サイトで配布されているロゴを使用しており、 このロゴはGoogleによってCC-BY 3.0の下で配布されています
- 冒頭の画像はいらすとやさんの画像を使っています。いつもありがとうございます