あずんひの日

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

Rust 1.63を早めに深掘り

こんにちは、あずんひ(@aznhe21)です。今月からあずんひの活動を支援できる仕組みを導入してみました。寄付頂ける方は記事末尾からお願いします。

さて、本日8/12(金)にRust 1.63がリリースされました。 この記事ではRust 1.63での変更点を詳しく紹介します。

8/12は最初の「PC」であるIBM 5150が発売された日 The PC 1.63

ピックアップ

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

スコープ限定スレッドが使えるようになった

スコープの中で完結するスレッドが導入され、'staticではないデータを使った並列計算ができるようになりました。

use std::thread;
use itertools::Itertools;

fn main() {
  let arr = vec![1, 2, 3];
  let mut sum = 0;
  thread::scope(|s| {
    println!("計算中...");
    s.spawn(|| sum = arr.iter().sum());

    println!("ちょっと待ってね");
  }); // thread::scopeが終わる時点で、中で起動されたスレッドはすべて待機・結合される

  // thread::spawnを直接使う場合、arrの再利用やmove無しでのsumの変更はできない
  // また、スレッドの完了を待機するには手動でjoinを呼び出す必要がある
  // let handle = thread::spawn(move || sum = arr.iter().sum());
  // handle.join().unwrap();

  println!("{} = {sum}", arr.iter().join(" + "));
}

std::thread::scopeによってスコープを生成し、その中で起動したスレッドはスコープから出る際にすべて結合されます。 これにより、起動元のスレッドにあるデータを可変借用した後、また元のスレッドでその結果を利用することができるのです。 この特性上、各クロージャにはmoveは付ける必要はありません(付けるとむしろエラーで混乱します)。

なお、thread::scope()内で起動したスレッドがパニックした場合、thread::scopeはパニックします。 これを防ぐには手動でjoinを呼び出す必要があります。

use std::thread;

fn main() {
  thread::scope(|s| {
    let handle = s.spawn(|| panic!("ワニワニ"));

    println!("{}パニック", handle.join().unwrap_err().downcast::<&str>().unwrap());
  });
}

関数を使って配列を初期化できるようになった

地味に便利系のAPIとしてcore::array::from_fnが加わりました。 これにより固定長配列の各要素を関数から生成できます。

fn main() {
    const N: usize = 5;
    let x: [u32; N] = core::array::from_fn(|i| (N - i) as u32);

    // [5, 4, 3, 2, 1]
    println!("{:?}", x);
}

各種ロック機構が定数文脈で生成できるようになった

MutexCondvarRwLocknewメソッドが定数化され、定数文脈で使えるようになりました。 これによりlazy_staticonce-cellなどを使わず、これらをグローバル変数として直接持つことができるようになります。

use std::sync::Mutex;
use std::thread;

static DATA: Mutex<Vec<u32>> = Mutex::new(Vec::new());

fn main() {
    let t1 = thread::spawn(|| {
        for i in 0..10 {
            let mut v = DATA.lock().unwrap();
            v.push(i);
        }
    });

    let t2 = thread::spawn(|| {
        for i in 10..20 {
            let mut v = DATA.lock().unwrap();
            v.push(i);
        }
    });

    t1.join().unwrap();
    t2.join().unwrap();

    println!("{:?}", DATA.lock());
}

なお、これはRust 1.62で実装がfutexに切り替わったことによる恩恵です。

I/Oの生ハンドルが所有権に基づいて管理できるようになった

これまで、OSの生ハンドルを扱う際はAsRawFdUNIX系)やAsRawHandleWindows)などを使っていました。 しかしこれはハンドルのコピーに近く、生存期間が分かりにくいという問題がありました。

Rust 1.64では生ハンドルに所有権の仕組みを持ち込み、変数などと同じくライフタイムによって生存期間を制御するAPIが加わりました。 Fileなどリソースオブジェクトを、 各変換トレイトによってAsFdAsHandleを通して借用オブジェクトBorrowedFdBorrowedHandleにできる他、 Intoによって所有オブジェクトOwnedFdOwnedHandleに変換することもできます。

use std::fs::File;
#[cfg(unix)]
use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
#[cfg(windows)]
use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};

fn main() {
    let f = File::open("hoge.txt").unwrap();

    // 生ハンドルとして借用
    {
        #[cfg(unix)]
        let raw: BorrowedFd = f.as_fd();
        #[cfg(windows)]
        let raw: BorrowedHandle = f.as_handle();
    }

    // 所有権を持つ生ハンドルに変換
    #[cfg(unix)]
    let raw: OwnedFd = f.into();
    #[cfg(windows)]
    let raw: OwnedHandle = f.into();

    // ファイルはOwnedFd/OwnedHandleによって正常に閉じられる
}
プラットフォーム 対象 変換トレイト 所有オブジェクト 借用オブジェクト
UNIX ファイル記述子 AsFd OwnedFd BorrowedFd
Windows ファイルハンドル AsHandle OwnedHandle BorrowedHandle
Windows ソケット AsSocket OwnedSocket BorrowedSocket

Box<T>&Tの関係と同じように、FileOwnedFdの寿命を超えてBorrowedFdが生き残ることはできません。 また、OwnedFdがドロップする際にはファイル記述子を閉じてくれるという機能もあります。

安定化されたAPIのドキュメント

安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。

array::from_fn

原典

#[inline]
#[stable(feature = "array_from_fn", since = "1.63.0")]
pub fn from_fn<T, const N: usize, F>(mut cb: F) -> [T; N]
where
    F: FnMut(usize) -> T,
{ /* 実装は省略 */ }

各配列要素Tcbの呼び出しによって返される配列[T; N]を生成する。

引数

  • cb:引数として現在の配列インデックスが渡されるコールバック

サンプル

let array = core::array::from_fn(|i| i);
assert_eq!(array, [0, 1, 2, 3, 4]);

Box::into_pin

原典

impl<T: ?Sized, A: Allocator> Box<T, A> {
    #[stable(feature = "box_into_pin", since = "1.63.0")]
    #[rustc_const_unstable(feature = "const_box", issue = "92521")]
    pub const fn into_pin(boxed: Self) -> Pin<Self>
    where
        A: 'static,
    { /* 実装は省略 */ }
}

Box<T>Pin<Box<T>>に変換する。もしTUnpinを実装していない場合、*boxedはメモリにピン留めされムーブされることはなくなる。

この変換によるヒープへのメモリ確保はなく、変換はその場で行われる。

この変換はFromによっても可能である。

Box::into_pin(Box::new(x))によるBoxの生成とピン留めは、 より簡潔にBox::pin(x)と書くこともできる。 このinto_pinメソッドは、Box<T>が既にある場合、またはBox::new以外の手段で(ピン留めされた)Boxを生成する場合に便利である。

メモ

クレート側でFrom<Box<T>> for Pin<T>の実装を追加することは推奨されない。 これはPin::fromを呼び出す際に曖昧さが生じるためである。 このような不適切な実装例を以下に示す。

use std::pin::Pin;

struct Foo; // このクレートで定義されている型
impl From<Box<()>> for Pin<Foo> {
    fn from(_: Box<()>) -> Pin<Foo> {
        Pin::new(Foo)
    }
}

let foo = Box::new(());
let bar = Pin::from(foo);

BinaryHeap::try_reserve

原典

impl<T> BinaryHeap<T> {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

現在の長さよりも少なくともadditional分の要素多く容量を確保することを試みる。 メモリ操作関数は頻繁なメモリ確保を避けようと、より多くの領域を確保することがある。 try_reserveを呼び出した後の容量は、Ok(())が返ればself.len() + additional以上となる。 容量が十分確保されている場合は何もしない。

エラー

容量がオーバーフローした場合、またはメモリ操作関数が失敗を報告した場合、エラーが返される。

サンプル

use std::collections::BinaryHeap;
use std::collections::TryReserveError;

fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
    let mut heap = BinaryHeap::new();

    // メモリを事前確保する。できなければ抜ける
    heap.try_reserve(data.len())?;

    // この複雑な処理中にOOMが発生することはない
    heap.extend(data.iter());

    Ok(heap.pop())
}
use std::collections::BinaryHeap;
use std::collections::TryReserveError;

fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
    let mut heap = BinaryHeap::new();

    // メモリを事前確保する。できなければ抜ける
    heap.try_reserve(data.len())?;

    // この複雑な処理中にOOMが発生することはない
    heap.extend(data.iter());

    Ok(heap.pop())
}
find_max_slow(&[1, 2, 3]).expect("テストハーネスが12バイトでOOMを起こす謎");

BinaryHeap::try_reserve_exact

原典

impl<T> BinaryHeap<T> {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

現在の長さよりも少なくともadditional分の要素多く最小限の容量を確保することを試みる。 try_reserveとは異なり、頻繁なメモリ確保を避けようとして意図的な過剰確保をすることはない。 try_reserve_exactを呼び出した後の容量は、Ok(())が返ればself.len() + additional以上となる。 容量が十分確保されている場合は何もしない。

メモリ操作関数はコレクションが要求する以上の領域を確保するかもしれないことに注意されたい。 従って、容量が最低限となることは保証されない。 今後も挿入することが予期される場合はtry_reserveの使用が推奨される。

エラー

容量がオーバーフローした場合、またはメモリ操作関数が失敗を報告した場合、エラーが返される。

サンプル

use std::collections::BinaryHeap;
use std::collections::TryReserveError;

fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
    let mut heap = BinaryHeap::new();

    // メモリを事前確保する。できなければ抜ける
    heap.try_reserve_exact(data.len())?;

    // この複雑な処理中にOOMが発生することはない
    heap.extend(data.iter());

    Ok(heap.pop())
}
use std::collections::BinaryHeap;
use std::collections::TryReserveError;

fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
    let mut heap = BinaryHeap::new();

    // メモリを事前確保する。できなければ抜ける
    heap.try_reserve_exact(data.len())?;

    // この複雑な処理中にOOMが発生することはない
    heap.extend(data.iter());

    Ok(heap.pop())
}
find_max_slow(&[1, 2, 3]).expect("テストハーネスが12バイトでOOMを起こす謎");

OsString::try_reserve

原典

impl OsString {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    #[inline]
    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

指定されたOsStringにおいて、現在の長さよりも少なくとも長さ単位additional多く容量を確保することを試みる。 文字列は頻繁なメモリ確保を避けようと、より多くの領域を確保することがある。 try_reserveを呼び出した後の容量は、Ok(())が返ればself.len() + additional以上となる。 容量が十分確保されている場合は何もしない。

エンコーディングと容量の単位についてはOsStringの主要な文書情報を参照されたい。

エラー

容量がオーバーフローした場合、またはメモリ操作関数が失敗を報告した場合、エラーが返される。

サンプル

use std::ffi::{OsStr, OsString};
use std::collections::TryReserveError;

fn process_data(data: &str) -> Result<OsString, TryReserveError> {
    let mut s = OsString::new();

    // メモリを事前確保する。できなければ抜ける
    s.try_reserve(OsStr::new(data).len())?;

    // この複雑な処理中にOOMが発生することはない
    s.push(data);

    Ok(s)
}
use std::ffi::{OsStr, OsString};
use std::collections::TryReserveError;

fn process_data(data: &str) -> Result<OsString, TryReserveError> {
    let mut s = OsString::new();

    // メモリを事前確保する。できなければ抜ける
    s.try_reserve(OsStr::new(data).len())?;

    // この複雑な処理中にOOMが発生することはない
    s.push(data);

    Ok(s)
}
process_data("123").expect("テストハーネスが3バイトでOOMを起こす謎");

OsString::try_reserve_exact

原典

impl OsString {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    #[inline]
    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

指定されたOsStringにおいて、現在の長さよりも少なくとも長さ単位additional多く最小限の容量を確保することを試みる。 try_reserve_exactを呼び出した後の容量は、Ok(())が返ればself.len() + additional以上となる。 容量が十分確保されている場合は何もしない。

メモリ操作関数はOsStringが要求する以上の領域を確保するかもしれないことに注意されたい。 従って、容量が最低限となることは保証されない。 今後も挿入することが予期される場合はtry_reserveの使用が推奨される。

エンコーディングと容量の単位についてはOsStringの主要な文書情報を参照されたい。

エラー

容量がオーバーフローした場合、またはメモリ操作関数が失敗を報告した場合、エラーが返される。

サンプル

use std::ffi::{OsStr, OsString};
use std::collections::TryReserveError;

fn process_data(data: &str) -> Result<OsString, TryReserveError> {
    let mut s = OsString::new();

    // メモリを事前確保する。できなければ抜ける
    s.try_reserve_exact(OsStr::new(data).len())?;

    // この複雑な処理中にOOMが発生することはない
    s.push(data);

    Ok(s)
}
use std::ffi::{OsStr, OsString};
use std::collections::TryReserveError;

fn process_data(data: &str) -> Result<OsString, TryReserveError> {
    let mut s = OsString::new();

    // メモリを事前確保する。できなければ抜ける
    s.try_reserve_exact(OsStr::new(data).len())?;

    // この複雑な処理中にOOMが発生することはない
    s.push(data);

    Ok(s)
}
process_data("123").expect("テストハーネスが3バイトでOOMを起こす謎");

PathBuf::try_reserve

原典

impl PathBuf {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    #[inline]
    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

内包するOsStringインスタンスに対してtry_reserveを呼び出す。

PathBuf::try_reserve_exact

原典

impl PathBuf {
    #[stable(feature = "try_reserve_2", since = "1.63.0")]
    #[inline]
    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError>
    { /* 実装は省略 */ }
}

内包するOsStringインスタンスに対してtry_reserve_exactを呼び出す。

Path::try_exists

原典

impl Path {
    #[stable(feature = "path_try_exists", since = "1.63.0")]
    #[inline]
    pub fn try_exists(&self) -> io::Result<bool>
    { /* 実装は省略 */ }
}

パスが存在する実体を指している場合はOk(true)を返す。

この関数はシンボリックリンクを辿って対象ファイルに関する情報を問い合わせる。 シンボリックリンクが壊れている場合はOk(false)を返す。

exists()メソッドとは対照的に、このメソッドはパスが存在しないこと以外のエラーをスルーすることはない (例えば親ディレクトリのいずれかでパーミッションが拒否された場合にはErr(_)を返す)。

このメソッドによってexists()メソッドの落とし穴を避けることができるものの、 依然として「time-of-check to time-of-use」(TOCTOU、点検と行使の時間差)バグを防ぐことはできないことに注意されたい。 これらのバグが問題にならない状況でのみこのメソッドを使用すること。

サンプル

use std::path::Path;
assert!(!Path::new("ないよ.txt").try_exists().expect("ないよ.txtの存在を確認できない"));
assert!(Path::new("/root/秘密.txt").try_exists().is_err());

Ref::filter_map

原典

impl<'b, T: ?Sized> Ref<'b, T> {
    #[stable(feature = "cell_filter_map", since = "1.63.0")]
    #[inline]
    pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self>
    where
        F: FnOnce(&T) -> Option<&U>,
    { /* 実装は省略 */ }
}

借用したデータにおける任意の構成要素に対する新しいRefを生成する。 クロージャNoneを返した場合、元々の安全装置(guard)がErr(...)として返る。

RefCellは既に不変借用済みであるため、このメソッドは失敗しない。

これはRef::filter_map(...)として使う必要のある関連メソッドである。 メソッドではDerefを通して使用される、RefCellの内容に対する同名メソッドと干渉してしまう。

サンプル

use std::cell::{RefCell, Ref};

let c = RefCell::new(vec![1, 2, 3]);
let b1: Ref<Vec<u32>> = c.borrow();
let b2: Result<Ref<u32>, _> = Ref::filter_map(b1, |v| v.get(1));
assert_eq!(*b2.unwrap(), 2);

RefMut::filter_map

原典

impl<'b, T: ?Sized> RefMut<'b, T> {
    #[stable(feature = "cell_filter_map", since = "1.63.0")]
    #[inline]
    pub fn filter_map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, Self>
    where
        F: FnOnce(&mut T) -> Option<&mut U>,
    { /* 実装は省略 */ }
}

借用したデータにおける任意の構成要素に対する新しいRefMutを生成する。 クロージャNoneを返した場合、元々の安全装置(guard)がErr(...)として返る。

RefCellは既に可変借用済みであるため、このメソッドは失敗しない。

これはRefMut::filter_map(...)として使う必要のある関連メソッドである。 メソッドではDerefを通して使用される、RefCellの内容に対する同名メソッドと干渉してしまう。

サンプル

use std::cell::{RefCell, RefMut};

let c = RefCell::new(vec![1, 2, 3]);

{
    let b1: RefMut<Vec<u32>> = c.borrow_mut();
    let mut b2: Result<RefMut<u32>, _> = RefMut::filter_map(b1, |v| v.get_mut(1));

    if let Ok(mut b2) = b2 {
        *b2 += 2;
    }
}

assert_eq!(*c.borrow(), vec![1, 4, 3]);

NonNull::<[T]>::len

原典

impl<T> NonNull<[T]> {
    #[stable(feature = "slice_ptr_len_nonnull", since = "1.63.0")]
    #[rustc_const_stable(feature = "const_slice_ptr_len_nonnull", since = "1.63.0")]
    #[rustc_allow_const_fn_unstable(const_slice_ptr_len)]
    #[must_use]
    #[inline]
    pub const fn len(self) -> usize
    { /* 実装は省略 */ }
}

非NULLな生のスライスの長さを返す。

戻り値は要素数であり、バイト数ではない。

ポインタが有効なアドレスを持たないために非NULLな生のスライスをスライスに逆参照できない場合であっても、この関数は安全である。

サンプル

#![feature(nonnull_slice_from_raw_parts)]
use std::ptr::NonNull;

let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
assert_eq!(slice.len(), 3);

ToOwned::clone_into

原典

#[cfg_attr(not(test), rustc_diagnostic_item = "ToOwned")]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ToOwned {
    #[stable(feature = "toowned_clone_into", since = "1.63.0")]
    fn clone_into(&self, target: &mut Self::Owned)
    { /* 実装は省略 */ }
}

所有するデータを借用したデータで置き換えるときに使用する。大抵は複製して置き換える。

Clone::clone_fromに対して借用に汎化した版である。

サンプル

基本的な使い方

let mut s: String = String::new();
"こんにちは".clone_into(&mut s);

let mut v: Vec<i32> = Vec::new();
[1, 2][..].clone_into(&mut v);

Ipv6Addr::to_ipv4_mapped

原典

impl Ipv6Addr {
    #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
    #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline]
    pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr>
    { /* 実装は省略 */ }
}

このアドレスがIETF RFC 4291のセクション2.5.5.2で定義されたIPv4にマップされたアドレスの場合、 IPv4アドレスに変換する。そうでない場合はNoneを返す。

::ffff:a.b.c.da.b.c.dとなる。 ::ffffで始まらないすべてのアドレスではNoneを返す。

サンプル

use std::net::{Ipv4Addr, Ipv6Addr};

assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None);
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(),
           Some(Ipv4Addr::new(192, 10, 2, 255)));
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None);

unix::io::AsFd

原典

#[stable(feature = "io_safety", since = "1.63.0")]
pub trait AsFd {
    #[stable(feature = "io_safety", since = "1.63.0")]
    fn as_fd(&self) -> BorrowedFd<'_>;
}
Unixでのみ使用可能。

内包するオブジェクトからファイル記述子を借用するためのトレイト。

このトレイトはUNIXプラットフォームでのみ使用可能であり、メソッドを呼び出すためにはトレイトをインポートしておく必要がある。 Windowsプラットフォームでは対応するAsHandleAsSocketの2つのトレイトがある。

必須メソッド

fn as_fd(&self) -> BorrowedFd<'_>

ファイル記述子を借用する。

サンプル
use std::fs::File;

let mut f = File::open("foo.txt")?;
let borrowed_fd: BorrowedFd<'_> = f.as_fd();
use std::fs::File;
use std::io;
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsFd, BorrowedFd};
#[cfg(unix)]
use std::os::unix::io::{AsFd, BorrowedFd};

let mut f = File::open("foo.txt")?;
#[cfg(any(unix, target_os = "wasi"))]
let borrowed_fd: BorrowedFd<'_> = f.as_fd();
Ok::<(), io::Error>(())

unix::io::BorrowedFd<'fd>

原典

#[derive(Copy, Clone)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
#[rustc_nonnull_optimization_guaranteed]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct BorrowedFd<'fd>
{ /* フィールドは省略 */ }
Unixでのみ使用可能。

借用されたファイル記述子。

この構造体はファイル記述子を所有するものと寿命を紐付けるためにライフタイムパラメータを保持する。

この構造体はrepr(transparent)を使用していて主となるファイル記述子の表現を持つため、 ファイル記述子を引数として渡すようなFFIで使用することができる。 これはキャプチャも消費もされず、値が-1となることもない。

この型の.to_owned()実装はOwnedFdではなくもう一つのBorrowedFdを返す。 これは生のファイル記述子への些細な(trivial)コピーを作成し、同じライフタイムによって借用されるだけである。

unix::io::BorrowedFd<'fd>::borrow_raw

原典

impl BorrowedFd<'_> {
    #[inline]
    #[rustc_const_stable(feature = "io_safety", since = "1.63.0")]
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub const unsafe fn borrow_raw(fd: RawFd) -> Self
    { /* 実装は省略 */ }
}

指定された生のファイル記述しを保持するBorrowedFdを返す。

安全性

fdが指すリソースは返されたBorrowedFdが生存する間は開かれていなければならず、かつ値が-1であってはならない。

unix::io::BorrowedFd<'fd>::try_clone_to_owned

原典

impl BorrowedFd<'_> {
    #[cfg(not(target_arch = "wasm32"))]
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd>
    { /* 実装は省略 */ }
}

既存のBorrowedFdインスタンスと同じ内包するファイル記述子を共有する、新しいOwnedFdインスタンスを返す。

unix::io::OwnedFd

原典

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
#[rustc_nonnull_optimization_guaranteed]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct OwnedFd
{ /* フィールドは省略 */ }
Unixでのみ使用可能。

所有権を持つファイル記述子。

これはドロップ時にファイル記述子を閉じる。

この構造体はrepr(transparent)を使用していて主となるファイル記述子の表現を持つため、 ファイル記述子を消費された引数として渡す、または所有権を持つ値として返すようなFFIで使用することができる。 これはキャプチャも消費もされず、値が-1となることもない。

unix::io::OwnedFd::try_clone

原典

impl OwnedFd {
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone(&self) -> crate::io::Result<Self>
    { /* 実装は省略 */ }
}

既存のOwnedFdインスタンスと同じ内包するファイル記述子を共有する、新しいOwnedFdインスタンスを返す。

windows::io::AsHandle

原典

#[stable(feature = "io_safety", since = "1.63.0")]
pub trait AsHandle {
    #[stable(feature = "io_safety", since = "1.63.0")]
    fn as_handle(&self) -> BorrowedHandle<'_>;
}
Windowsでのみ使用可能。

内包するオブジェクトからハンドルを借用するためのトレイト。

必須メソッド

fn as_handle(&self) -> BorrowedHandle<'_>

ハンドルを借用する。

サンプル
use std::fs::File;
use std::os::windows::io::{AsHandle, BorrowedHandle};

let mut f = File::open("foo.txt")?;
let borrowed_handle: BorrowedHandle<'_> = f.as_handle();
use std::fs::File;
use std::io;
use std::os::windows::io::{AsHandle, BorrowedHandle};

let mut f = File::open("foo.txt")?;
let borrowed_handle: BorrowedHandle<'_> = f.as_handle();
Ok::<(), io::Error>(())

windows::io::BorrowedHandle<'handle>

原典

#[derive(Copy, Clone)]
#[repr(transparent)]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct BorrowedHandle<'handle>
{ /* フィールドは省略 */ }
Windowsでのみ使用可能。

借用されたハンドル。

この構造体はハンドルを所有するものと寿命を紐付けるためにライフタイムパラメータを保持する。

この構造体はrepr(transparent)を使用していて主となるハンドルの表現を持つため、 ハンドルを引数として渡すようなFFIで使用することができる。 これはキャプチャも消費もされない。

この値は-1となるかもしれないことに注意されたい。 BorrowedHandleは常に有効なハンドル値、例えば現在のプロセスハンドルなどを表す。 これはINVALID_HANDLE_VALUEと同じ値を持つものの、現在のプロセスハンドルの方は有効なのである。 詳細はこちら(※訳注:英語ページ)を参照されたい。

また、NULL(0)を持つこともある。 これはコンソールがプロセスから切り離されたとき、またはwindows_subsystemが使われているときに発生する。

この型の.to_owned()実装はOwnedHandleではなくもう一つのBorrowedHandleを返す。 これは生のハンドルへの些細な(trivial)コピーを作成し、同じライフタイムによって借用されるだけである。

windows::io::BorrowedHandle<'handle>::borrow_raw

原典

impl BorrowedHandle<'_> {
    #[inline]
    #[rustc_const_stable(feature = "io_safety", since = "1.63.0")]
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub const unsafe fn borrow_raw(handle: RawHandle) -> Self
    { /* 実装は省略 */ }
}

指定された生のハンドルを保持するBorrowedHandleを返す。

安全性

handleが指すリソースは有効な開かれたハンドルでなければならず、 また返されたBorrowedHandleが生存する間は開かれていなければならない。

値がINVALID_HANDLE_VALUE(-1)かもしれないことに注意されたい。 これは有効なハンドルの場合がある。詳細はこちら(※訳注:英語ページ)を参照されたい。

また、NULL(0)を持つこともある。 これはコンソールがプロセスから切り離されたとき、またはwindows_subsystemが使われているときに発生する。

windows::io::BorrowedHandle<'handle>::try_clone_to_owned

原典

impl BorrowedHandle<'_> {
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedHandle>
    { /* 実装は省略 */ }
}

既存のBorrowedHandleインスタンスと同じ内包するファイル記述子を共有する、新しいOwnedHandleインスタンスを返す。

windows::io::OwnedHandle

原典

#[repr(transparent)]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct OwnedHandle
{ /* フィールドは省略 */ }
Windowsでのみ使用可能。

所有権を持つハンドル。

これはドロップ時にハンドルを閉じる。

この値は-1となるかもしれないことに注意されたい。 OwnedHandleは常に有効なハンドル値、例えば現在のプロセスハンドルなどを表す。 これはINVALID_HANDLE_VALUEと同じ値を持つものの、現在のプロセスハンドルの方は有効なのである。 詳細はこちら(※訳注:英語ページ)を参照されたい。

また、NULL(0)を持つこともある。 これはコンソールがプロセスから切り離されたとき、またはwindows_subsystemが使われているときに発生する。

OwnedHandleはドロップ時にCloseHandleを使ってハンドルを閉じる。 そのため、代わりにRegCloseKeyで閉じる必要のあるレジストリキーを開くハンドルでは使用してはならない。

windows::io::OwnedHandle::try_clone

原典

impl OwnedHandle {
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone(&self) -> crate::io::Result<Self>
    { /* 実装は省略 */ }
}

既存のOwnedHandleインスタンスと同じ内包するファイル記述子を共有する、新しいOwnedHandleインスタンスを返す。

windows::io::HandleOrInvalid

原典

#[repr(transparent)]
#[stable(feature = "io_safety", since = "1.63.0")]
#[derive(Debug)]
pub struct HandleOrInvalid(/* フィールドは省略 */);
Windowsでのみ使用可能。

CreateFileWの戻り値のようなINVALID_HANDLE_VALUEがエラーを示す番兵値として使われる、 戻り値または出力用引数のハンドルのためのFFI型。 この構造体はrepr(transparent)を使用していて主となるハンドルの表現を持つため、 こういったFFI宣言で使用することができる。

HandleOrInvalidを使う上で唯一の有用なことは、TryFromの実装によって値をOwnedHandleに変換することである。 この変換はINVALID_HANDLE_VALUEのチェックを世話してくれる。 これにより、最初にINVALID_HANDLE_VALUEをチェックしなければこのようなFFI呼び出しが使用できないことを保証する。

この型はOwnedHandleが保持できる任意のハンドル値を保持できる。 ただし、-1を保持する場合はINVALID_HANDLE_VALUEとして解釈される。

もしINVALID_HANDLE_VALUE以外のハンドルを保持する場合、ドロップ時にハンドルを閉じる。

windows::io::HandleOrInvalid::from_raw_handle

原典

    #[stable(feature = "io_safety", since = "1.63.0")]
    #[inline]
    pub unsafe fn from_raw_handle(handle: RawHandle) -> Self
    { /* 実装は省略 */ }
}

CreateFileWのような失敗を示すためにINVALID_HANDLE_VALUEを使用するWindows APIから返されたRawHandleにより、 Selfの新しいインスタンスを生成する。

NULLを使って失敗を示すようなAPIではHandleOrInvalidの代わりにHandleOrNullを使用すること。

安全性

渡されたhandleFromRawHandle::from_raw_handleにおける安全性要件を満たすか、 値がINVALID_HANDLE_VALUE(-1)でなければならない。 すべてのWindows APIがエラーのためにINVALID_HANDLE_VALUEを使用するわけではないことに注意されたい。 詳細はこちら(※訳注:英語ページ)を参照されたい。

windows::io::HandleOrNull

原典

#[repr(transparent)]
#[stable(feature = "io_safety", since = "1.63.0")]
#[derive(Debug)]
pub struct HandleOrNull(/* フィールドは省略 */);
Windowsでのみ使用可能。

CreateThreadの戻り値のようなNULLがエラーを示す番兵値として使われる、 戻り値または出力用引数のハンドルのためのFFI型。 この構造体はrepr(transparent)を使用していて主となるハンドルの表現を持つため、 こういったFFI宣言で使用することができる。

HandleOrNullを使う上で唯一の有用なことは、TryFromの実装によって値をOwnedHandleに変換することである。 この変換はNULLのチェックを世話してくれる。 これにより、最初にNULLをチェックしなければこのようなFFI呼び出しが使用できないことを保証する。

この型はOwnedHandleが保持できる任意のハンドル値を保持できる。 OwnedHandleと同様、-1を保持する場合は有効なハンドル値、 例えば(INVALID_HANDLE_VALUEではなく)現在のプロセスハンドルなどとして解釈される。

もし非NULLのハンドルを保持する場合、ドロップ時にハンドルを閉じる。

windows::io::HandleOrNull::from_raw_handle

[原典][windows::io::HandleOrNull::from_raw_handle]

    #[stable(feature = "io_safety", since = "1.63.0")]
    #[inline]
    pub unsafe fn from_raw_handle(handle: RawHandle) -> Self
    { /* 実装は省略 */ }
}

CreateThreadのような失敗を示すためにNULLを使用するWindows APIから返されたRawHandleにより、 Selfの新しいインスタンスを生成する。

INVALID_HANDLE_VALUEを使って失敗を示すようなAPIではHandleOrNullの代わりにHandleOrInvalidを使用すること。

安全性

渡されたhandleFromRawHandle::from_raw_handleにおける安全性要件を満たすか、 値がNULLでなければならない。 すべてのWindows APIがエラーのためにNULLを使用するわけではないことに注意されたい。 詳細はこちら(※訳注:英語ページ)を参照されたい。

windows::io::InvalidHandleError

原典

#[stable(feature = "io_safety", since = "1.63.0")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidHandleError(/* フィールドは省略 */);
Windowsでのみ使用可能。

これはHandleOrInvalidによってハンドルに変換する際に使われるエラー型で、 値がINVALID_HANDLE_VALUEであることを示す。

windows::io::NullHandleError

原典

#[stable(feature = "io_safety", since = "1.63.0")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NullHandleError(/* フィールドは省略 */);
Windowsでのみ使用可能。

これはHandleOrNullによってハンドルに変換する際に使われるエラー型で、 値がNULLであることを示す。

windows::io::AsSocket

原典

#[stable(feature = "io_safety", since = "1.63.0")]
pub trait AsSocket {
    #[stable(feature = "io_safety", since = "1.63.0")]
    fn as_socket(&self) -> BorrowedSocket<'_>;
}
Windowsでのみ使用可能。

内包するオブジェクトからソケットを借用するためのトレイト。

必須メソッド

fn as_socket(&self) -> BorrowedSocket<'_>

ソケットを借用する。

windows::io::BorrowedSocket<'handle>

原典

#[derive(Copy, Clone)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
#[cfg_attr(
    target_pointer_width = "64",
    rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
)]
#[rustc_nonnull_optimization_guaranteed]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct BorrowedSocket<'socket>
{ /* フィールドは省略 */ }
Windowsでのみ使用可能。

借用されたソケット。

この構造体はソケットを所有するものと寿命を紐付けるためにライフタイムパラメータを保持する。

この構造体はrepr(transparent)を使用していて主となるソケットの表現を持つため、 ソケットを引数として渡すようなFFIで使用することができる。 これはキャプチャも消費もされず、値がINVALID_SOCKETとなることもない。

この型の.to_owned()実装はOwnedSocketではなくもう一つのBorrowedSocketを返す。 これは生のソケットへの些細な(trivial)コピーを作成し、同じライフタイムによって借用されるだけである。

windows::io::BorrowedSocket<'handle>::borrow_raw

原典

impl BorrowedSocket<'_> {
    #[inline]
    #[rustc_const_stable(feature = "io_safety", since = "1.63.0")]
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub const unsafe fn borrow_raw(socket: RawSocket) -> Self
    { /* 実装は省略 */ }
}

指定された生のソケットを保持するBorrowedSocketを返す。

安全性

socketが指すリソースは返されたBorrowedSocketが生存する間は開かれていなければならず、 かつ値がINVALID_SOCKETであってはならない。

windows::io::BorrowedSocket<'handle>::try_clone_to_owned

原典

impl BorrowedSocket<'_> {
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket>
    { /* 実装は省略 */ }
}

既存のBorrowedSocketインスタンスと同じ内包するソケットを共有する、新しいOwnedSocketインスタンスを返す。

windows::io::OwnedSocket

原典

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
// This is -2, in two's complement. -1 is `INVALID_SOCKET`.
#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
#[cfg_attr(
    target_pointer_width = "64",
    rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
)]
#[rustc_nonnull_optimization_guaranteed]
#[stable(feature = "io_safety", since = "1.63.0")]
pub struct OwnedSocket
{ /* フィールドは省略 */ }
Windowsでのみ使用可能。

所有権を持つソケット。

これはドロップ時にソケットを閉じる。

この構造体はrepr(transparent)を使用していて主となるソケットの表現を持つため、 ソケットを消費された引数として渡す、または所有権を持つ値として返すようなFFIで使用することができる。 これは値がINVALID_SOCKETとなることはない。

windows::io::OwnedSocket::try_clone

原典

impl OwnedSocket {
    #[stable(feature = "io_safety", since = "1.63.0")]
    pub fn try_clone(&self) -> io::Result<Self>
    { /* 実装は省略 */ }
}

既存のOwnedSocketインスタンスと同じ内包するファイル記述子を共有する、新しいOwnedSocketインスタンスを返す。

thread::scope

原典

#[track_caller]
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn scope<'env, F, T>(f: F) -> T
where
    F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
{ /* 実装は省略 */ }

スコープ限定スレッドを起動するためのスコープを生成する。

scopeに渡された関数にはScopeオブジェクトが渡される。 このオブジェクトを通してスコープ限定スレッドを起動することができる。

スコープ限定でないスレッドと違い、 スコープ限定スレッドはスコープの終わりですべてのスレッドが結合(join)されることが保証されているため、 非'staticなデータを借用することができる。

スコープ内で起動されたすべてのスレッドのうち手動で結合されていないものはすべて、 この関数が返る前に自動で結合される。

パニック

自動的に結合されたスレッドのうちいずれかがパニックしたとき、この関数はパニックする。

起動されたスレッドのパニックを処理したい場合、スコープの終わりまでにそれらをjoinすること。

サンプル

use std::thread;

let mut a = vec![1, 2, 3];
let mut x = 0;

thread::scope(|s| {
    s.spawn(|| {
        println!("1つ目に起動されたスレッドからこんにちは");
        // ここで`a`を借用できる
        dbg!(&a);
    });
    s.spawn(|| {
        println!("2つ目に起動されたスレッドからこんにちは");
        // 他のスレッドが使用していないため、
        // ここで`x`を可変借用することもできる
        x += a[0] + a[2];
    });
    println!("主スレッドからこんにちは");
});

// スコープのあとではもう一度変数の更新とアクセスができる
a.push(4);
assert_eq!(x, a.len());

ライフタイム

スコープ限定スレッドでは2つのライフタイムを伴う。'scope'envである。

ライフタイム'scopeはスコープそのもののライフタイムを表す。 つまり、新しいスコープ限定スレッドが起動されうる期間と、 それらスレッドが実行中かもしれない期間である。 このライフタイムが尽きたとき、すべてのスコープ限定スレッドは結合される。 このライフタイムは、scope関数内でfscopeへの引数)が始まる前に開始される。 また、fが戻りすべてのスコープ限定スレッドが結合された後、scopeが戻る前に終了する。

ライフタイム'envはスコープ限定スレッドが借用するもののライフタイムを表す。 このライフタイムはscopeより長生きしなければならず、従って'scopeより短くすることはできない。 これはscopeの呼び出しと同じくらい小さくすることができる。 つまり、スコープの前に定義された局所変数など、 scopeの呼び出しより長生きするものはすべてスコープ限定スレッドで借用することができる。

'env: 'scope束縛はScope型の定義の一部である。

thread::Scope

原典

#[stable(feature = "scoped_threads", since = "1.63.0")]
pub struct Scope<'scope, 'env: 'scope>
{ /* フィールドは省略 */ }

中でスコープ限定スレッドを起動するためのスコープ。

詳細はscopeを参照されたい。

thread::Scope::spawn

原典

impl<'scope, 'env> Scope<'scope, 'env> {
    #[stable(feature = "scoped_threads", since = "1.63.0")]
    pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
    where
        F: FnOnce() -> T + Send + 'scope,
        T: Send + 'scope,
    { /* 実装は省略 */ }
}

スコープ限定でスレッドを起動し、そのスレッド用のScopedJoinHandleを返す。

スコープ限定でないスレッドと違い、この関数で起動するスレッドは外側のスコープから非'staticなデータを借用することができる。 詳細はscopeを参照されたい。

結合用ハンドルは起動したスレッドを結合するのに使えるjoinメソッドを提供する。 起動したスレッドがパニックした場合、joinはパニックのペイロードを含むErrを返す。

結合用ハンドルがドロップすると、起動したスレッドはスコープの終わりで暗黙的に結合される。 この場合、起動したスレッドがパニックすると、すべてのスレッドが結合されたあとでscopeがパニックする。

この呼び出しはデフォルトのパラメータを用いたBuilderを使ってスレッドを生成する。 スタックサイズやスレッド名を指定したい場合、代わりにBuilder::spawn_scopedを使用する。

パニック

OSがスレッドを生成するのに失敗した場合にパニックする。 そのようなエラーから回復するにはBuilder::spawn_scopedを使用する。

thread::ScopedJoinHandle

原典

#[stable(feature = "scoped_threads", since = "1.63.0")]
pub struct ScopedJoinHandle<'scope, T>(/* フィールドは省略 */);

スコープ限定スレッドを結合(終了までブロック)するための所有権を持つアクセス権。

詳細はScope::spawnを参照されたい。

thread::ScopedJoinHandle::thread

原典

impl<'scope, T> ScopedJoinHandle<'scope, T> {
    #[must_use]
    #[stable(feature = "scoped_threads", since = "1.63.0")]
    pub fn thread(&self) -> &Thread
    { /* 実装は省略 */ }
}

内包するスレッドのハンドルを抽出する。

サンプル

use std::thread;

thread::scope(|s| {
    let t = s.spawn(|| {
        println!("こんにちは");
    });
    println!("スレッドID:{:?}", t.thread().id());
});

thread::ScopedJoinHandle::join

原典

impl<'scope, T> ScopedJoinHandle<'scope, T> {
    #[stable(feature = "scoped_threads", since = "1.63.0")]
    pub fn join(self) -> Result<T>
    { /* 実装は省略 */ }
}

連携スレッドの完了を待つ。

連携スレッドが既に完了している場合、この関数は即座に戻る。

不可分なメモリ順序(※訳注:英語ページ)に関して言えば、連携スレッドの完了はこの関数の復帰と同期している。 つまり、そのスレッドでの操作がすべて発生してからjoin後の操作が発生する。

連携スレッドがパニックした場合、パニックのペイロードと共にErrが返る。

サンプル

use std::thread;

thread::scope(|s| {
    let t = s.spawn(|| {
        panic!("あちゃー");
    });
    assert!(t.join().is_err());
});

thread::ScopedJoinHandle::is_finished

原典

impl<'scope, T> ScopedJoinHandle<'scope, T> {
    #[stable(feature = "scoped_threads", since = "1.63.0")]
    pub fn is_finished(&self) -> bool
    { /* 実装は省略 */ }
}

連携スレッドが主関数の実行を完了したかどうかを確認する。

is_finishedをチェックし、その値がfalseだった場合にjoinを呼び出すことで、 ブロックしない結合操作の実装をサポートする。 この関数はブロックしない。スレッドの完了を待機する間ブロックするにはjoinを使用する。

このメソッドはスレッドの主関数が返ったあと、スレッド自体が実行を停止する前の短い期間にtrueを返す場合がある。 とは言え、このメソッドがtrueを返せば、長時間ブロックすることなくjoinがすぐに戻るはずである。

変更点リスト

言語

コンパイラ

※Rustのティア付けされたプラットフォームサポートの詳細はPlatform Supportのページ(※訳注:英語)を参照

ライブラリ

安定化されたAPI

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

Cargo

互換性メモ

内部の変更

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

関連リンク

さいごに

次のリリースのRust 1.64は9/23(金)にリリースされる予定です。 1.64ではif式でのletによる束縛が複数回使えるようになる(if let Some(x) = a && let Some(y) = b)予定です。

ライセンス表記

  • この記事はApache 2/MITのデュアルライセンスで公開されている公式リリースノート及びドキュメントから翻訳・追記をしています
  • 冒頭の画像中にはRust公式サイトで配布されているロゴを使用しており、 このロゴはRust財団によってCC-BYの下で配布されています
  • 冒頭の画像はいらすとやさんの画像を使っています。いつもありがとうございます

活動支援