あずんひの日

あずんひの色々を書き留めるブログ

Rust 1.64を早めに深掘り

こんにちは、あずんひ(@aznhe21)です。8月25日に発売された地球防衛軍6がシリーズ中最も濃密なストーリーで非常に楽しめたのでおすすめです。

さて、本日9/23(金)にRust 1.64がリリースされました。 この記事ではRust 1.64での変更点を詳しく紹介します。 よろしければ記事末尾から活動支援頂けると嬉しいです。

なお、Rust 1.64の目玉(?)であったlet-chains(if式でのletによる束縛が複数回使える)はバグのため安定化が見送られました

9/23はFirefoxとAndroidの初版公開日 Firefox・AndroidとRustのマッチング

ピックアップ

個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。

libstdにあるFFI系の型がlibcore・liballocに移動した

多くのFFI系の型がlibstdだけではなくlibcore・liballocでも使えるようになりました。 これにより、組み込み系の環境でもCとの連携がやりやすくなります。

CStr及び関連する型がlibcoreで、 またCString及び関連する型がliballocに移動しました。

また、次の型がstd::os::rawだけでなくcore::ffistd::ffiで使えるようになりました。

.await時にIntoFutureが使われるようになった

これまでxxx.awaitにおけるxxxにはFutureトレイトを実装する型を受け入れていました。 Rust 1.64から、xxxにはFutureの代わりにIntoFutureを受け入れるようになります。

これは「非同期ビルダー」で真価を発揮します。 例えばreqwestクレートやsurfクレートではリクエストを生成するRequestBuilderからリクエストを送信する場合に.send()という文が必要でしたが、 RequestBuilderIntoFutureを実装すればこれが不要になります。

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-filereadmeワークスペースのルートからの相対パスとして解釈されます。 また、includeexcludeはサブクレートからの相対パスとして解釈されます。

  • 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を手動で実装する場合、大抵はFutureIntoFutureのどちらを型に実装するかを選択することになる。 ほとんどの場合には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をトレイト境界に使うことで、関数をFutureIntoFutureに対して汎化することができる。 これは関数の利用者にとっては便利であり、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;
}
Windowsでのみ使用可能。

fs::FileTypeへの、Windows限定の拡張。

Windowsでは、シンボリックリンクそれ自体がファイルかディレクトリであるかを知っている。

必須のメソッド

fn is_symlink_dir(&self) -> bool

このFileTypeシンボリックリンクであり、かつディレクトリである場合にtrueを返す。

fn is_symlink_file(&self) -> bool

このFileTypeシンボリックリンクであり、かつファイルである場合にtrueを返す。

変更点リスト

言語

コンパイラ

ライブラリ

安定化されたAPI

これらの型はstd::ffiで安定化されていたが、coreallocでも使えるようになった。

これらの型はstd::os::rawで安定化されていたが、core::ffistd::ffiでも使えるようになった。

以下のAPIが定数文脈で使えるようになった。

Cargo

その他

互換性メモ

内部の変更

これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。

関連リンク

さいごに

次のリリースの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 }

ライセンス表記