本日6/2(金)にリリースされたRust 1.70の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
- ピックアップ
- 最近のrust-analyzer
- 安定化されたAPIのドキュメント
- NonZero*::MIN
- NonZero*::MAX
- BinaryHeap::retain
- Rc::into_inner
- Arc::into_inner
- std::cell::OnceCell
- Option::is_some_and
- NonNull::slice_from_raw_parts
- Result::is_ok_and
- Result::is_err_and
- std::sync::atomic::Atomic*::as_ptr
- std::io::IsTerminal
- std::os::linux::net::SocketAddrExt
- std::os::unix::net::UnixDatagram::bind_addr
- std::os::unix::net::UnixDatagram::connect_addr
- std::os::unix::net::UnixDatagram::send_to_addr
- std::os::unix::net::UnixListener::bind_addr
- std::path::Path::as_mut_os_str
- std::sync::OnceLock
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
一度だけ初期化されるグローバル変数を書けるようになった
once_cellクレートにあったunsync::OnceCell
やsync::OnceCell
が標準ライブラリに取り込まれ、
それぞれstd::cell::OnceCell
とstd::sync::OnceLock
として使えるようになりました。
これらは一度だけ初期化される変数のための型でOnceCell
はスレッド安全ではないもの、OnceLock
はスレッド安全なものであり、
とりわけ後者はグローバル変数として使えるため、定数文脈で扱えない値をグローバル化するのに便利です。
ただし使う度に初期化処理を書く必要があるのが玉に瑕です。
これまでもMutex<Option<T>>
を使って似たようなことができましたが、
VAR.lock().unwrap().get_or_insert_with(|| Some(...))
と長い上に値が使われる間ロックされ続けるというのが欠点でした。
OnceLock
ならVAR.get_or_init(|| ...)
と短く、しかも戻り値は不変参照であるためロックがごく短い期間で済みます。
use std::sync::OnceLock; static OS_NAME: OnceLock<String> = OnceLock::new(); fn main() { // `OS_NAME`はどちらかのスレッドで一度だけ初期化され、上書きされることはない let t1 = std::thread::spawn(|| println!("{}", OS_NAME.get_or_init(read_os_name))); let t2 = std::thread::spawn(|| some_process(OS_NAME.get_or_init(read_os_name))); t1.join().unwrap(); t2.join().unwrap(); } fn read_os_name() -> String { use std::io::BufRead; for line in std::io::BufReader::new(std::fs::File::open("/etc/os-release").unwrap()).lines() { let line = line.unwrap(); if line.starts_with("NAME=\"") && line.ends_with('"') { return line[6..][..line.len() - 6 - 1].to_string(); } } "unknown".to_string() } fn some_process(_os_name: &str) { // ... }
Nightlyでは初期化用関数を合わせて保持して簡潔に書ける(lazy_staticクレートやonce_cell::sync::Lazy
に相当する)型である
std::cell::LazyCell
・std::sync::LazyLock
がありますが、変性や関数の型を指定する方法に懸念があるため
安定化はまだ先のようです。
format_args!
がコンパイル時に展開されるようになった
format_args!
やそれに依存するマクロ(write!
やprintln!
、log::info!
など)で数値や文字列などがコンパイル時に展開されるようになりました。
これにより出力コードが最適化され、高速化やバイナリサイズの縮小などが期待されます。
fn main() { // 数値や文字列リテラルは展開され・・・ println!("{} {}", 0, "hoge"); // このように書くのとほぼ同じ意味となる println!("0 hoge"); // ネストしたformat_args!も可能な限り展開され・・・ println!("{}", format_args!("{} {}", "nargs", std::env::args().len())); // こちらと同じ意味となる println!("nargs {}", std::env::args().len()); }
またformat_args!
の戻り値であるstd::fmt::Arguments
のas_str
メソッドにも影響があり、
これまでは引数が与えられていない場合のみ値が得られたところ、Rust 1.70からは展開される場合にも文字列リテラルを得られるようになります。
fn main() { // file!とline!はそれぞれ文字列と数値と解釈される結果展開可能なため、 // Rust 1.70からは展開された文字列リテラルを得られる let s = format_args!("log@{}:{}", file!(), line!()).as_str(); assert_eq!(s, Some("log@src/main.rs:4")); }
OptionやResult内の値を使って判定処理を書けるようになった
Option::is_some_and
により、Option
がSome
の時にその内部の値を使って判定をするといったことができるようになりました。
Result
でもResult::is_ok_and
で値がOk
のとき、Result::is_err_and
で値がErr
のときの判定ができます。
これまでもmap_or
やmatches!
マクロなどを使っても同じ処理が書けましたが、こちらの方が読みやすいかもしれません。
fn main() { let x = Some("hoge".to_string()); // 全部同じ意味 assert!(x.as_ref().map_or(false, |v| v.starts_with("hoge"))); assert!(matches!(&x, Some(v) if v.starts_with("hoge"))); assert!(x.as_ref().is_some_and(|v| v.starts_with("hoge"))); }
Cargoでの高速なインデックス更新が既定で使用されるようになった
Rust 1.68で導入されたレジストリ用の疎(sparse)なプロトコルが既定で使用されるようになり、 何も設定しなくてもインデックス更新が高速化されるようになりました。
何らかの理由によりこのプロトコルを使用できない場合、環境変数CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git
を設定するか、
~/.cargo/config.toml
で以下のように設定することで古いプロトコルを使用することができます。
[registries.crates-io] protocol = 'git'
最近のrust-analyzer
最近rust-analyzerに入った変更の中から、個人的に気になったものをピックアップしました。
安定化されていないAPIが候補に出なくなった
2023-04-17(v0.3.1481)で入った機能ですが、 バグのため使えるようになったのは2023-04-24(v0.3.1489)からです。
補完候補に出ていた安定化前のAPIが候補に出なくなりました。 使えない候補が出てくるのは地味にストレスだったので嬉しい変更です。
なお、rustupでNightlyを使う設定ではきちんと安定化前の候補も出てきます。
クロージャが何をキャプチャしているか詳細に分かるようになった
2023-05-01(v0.3.1498)・2023-05-08(v0.3.1506)・ 2023-05-15(v0.3.1514)・2023-05-22(v0.3.1524)での変更です。
クロージャのかざし(hover)ヒントで、そのクロージャが変数をどのようにキャプチャしているかが分かるようになりました。
なおこのヒントは変数ではなくクロージャ自体のものであり、カーソルが|
の上である必要があります。
またカーソルがmove
や引数を囲う|
の上にある際、そのクロージャがキャプチャする変数がハイライトされるようになりました。
また既定では無効であるものの、VS Codeなど一部エディタでははめ込み(inlay)ヒントにも情報が表示されるようです。
クロージャのキャプチャする全変数が一目で分かる他かざしヒントでは変数ごとにムーブや可変参照といった種類が分かるため、
クロージャをFnOnce
にしたいのにFnMut
になってしまう場合に原因が分かるなど便利です。
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
NonZero*::MIN
impl NonZeroI8 { #[stable(feature = "nonzero_min_max", since = "1.70.0")] pub const MIN: Self = Self::new(i8::MIN).unwrap(); }
この非ゼロ整数型で表現できる最小の値で、i8::MIN
と同等。
注:整数型は原則としてMIN
からMAX
までの全整数を定めるが、符号付きの非ゼロ整数は特例であり、
0に「途切れ」がある。
サンプル
assert_eq!(NonZeroI8::MIN.get(), i8::MIN);
use std::num::NonZeroI8; assert_eq!(NonZeroI8::MIN.get(), i8::MIN);
NonZero*::MAX
impl NonZeroI8 { #[stable(feature = "nonzero_min_max", since = "1.70.0")] pub const MAX: Self = Self::new(i8::MAX).unwrap(); }
この非ゼロ整数型で表現できる最大の値で、i8::MAX
と同等。
注:整数型は原則としてMIN
からMAX
までの全整数を定めるが、符号付きの非ゼロ整数は特例であり、
0に「途切れ」がある。
サンプル
assert_eq!(NonZeroI8::MAX.get(), i8::MAX);
use std::num::NonZeroI8; assert_eq!(NonZeroI8::MAX.get(), i8::MAX);
BinaryHeap::retain
impl<T: Ord> BinaryHeap<T> { #[stable(feature = "binary_heap_retain", since = "1.70.0")] pub fn retain<F>(&mut self, mut f: F) where F: FnMut(&T) -> bool, { /* 実装は省略 */ } }
述語で指定された要素のみを保持する。
つまり、要素e
においてf(&e)
がfalse
を返すすべての要素を削除する。
要素はソートされていない(かつ不特定の)順序で渡される。
サンプル
use std::collections::BinaryHeap; let mut heap = BinaryHeap::from([-10, -5, 1, 2, 4, 13]); heap.retain(|x| x % 2 == 0); // 偶数だけ保持 assert_eq!(heap.into_sorted_vec(), [-10, 2, 4])
Rc::into_inner
impl<T> Rc<T> { #[inline] #[stable(feature = "rc_into_inner", since = "1.70.0")] pub fn into_inner(this: Self) -> Option<T> { /* 実装は省略 */ } }
Rc
が強参照を1つだけ持つ場合に内部の値を返す。
さもなくばNone
が返されRc
はドロップする。
このRc
を指す弱参照があったとしてもこのメソッドは成功する。
このRc
の各複製に対して(※訳注:同時に)Rc::into_inner
が呼ばれたとしても、どれか1つの呼び出しだけが内部の値を返すことが保証される。
つまるところ内部の値がドロップすることはない。
これはRc::try_unwrap(this).ok()
と同等である(ただしRc
には適用されない競合状態のためArc
においては同等ではない)。
Arc::into_inner
impl<T> Arc<T> { #[inline] #[stable(feature = "arc_into_inner", since = "1.70.0")] pub fn into_inner(this: Self) -> Option<T> { /* 実装は省略 */ } }
Arc
が強参照を1つだけ持つ場合に内部の値を返す。
さもなくばNone
が返されArc
はドロップする。
このArc
を指す弱参照があったとしてもこのメソッドは成功する。
このArc
の各複製に対して(※訳注:同時に)Arc::into_inner
が呼ばれたとしても、
どれか1つの呼び出しだけが内部の値を返すことが保証される。
つまるところ内部の値がドロップすることはない。
同様の式であるArc::try_unwrap(this).ok()
にこの保証はない。
下記サンプルの最後やArc::try_unwrap
の文書も参照のこと。
サンプル
Arc::into_inner
がもたらす保証についての最小サンプル。
use std::sync::Arc; let x = Arc::new(3); let y = Arc::clone(&x); // `Arc`の両複製に対して2つのスレッドが`Arc::into_inner`を呼び出す let x_thread = std::thread::spawn(|| Arc::into_inner(x)); let y_thread = std::thread::spawn(|| Arc::into_inner(y)); let x_inner_value = x_thread.join().unwrap(); let y_inner_value = y_thread.join().unwrap(); // どちらかのスレッドが内部の値を受け取ることが保証される assert!(matches!( (x_inner_value, y_inner_value), (None, Some(3)) | (Some(3), None) )); // 各スレッドで代わりに`Arc::try_unwrap(x).ok()`や`Arc::try_unwrap(y).ok()`を // 呼び出した場合、結果が`(None, None)`となる可能性がある
Arc::into_inner
の必要性を示すより実践的なサンプル。
use std::sync::Arc; // `Arc`を使った単純かつ単一なリンクリストの定義 #[derive(Clone)] struct LinkedList<T>(Option<Arc<Node<T>>>); struct Node<T>(T, Option<Arc<Node<T>>>); // `Arc`のデストラクタに依存する長大な`LinkedList<T>`をドロップすると // スタックオーバーフローが発生する可能性がある。これを防ぐため、 // ループで破棄を行う`Drop`を実装する impl<T> Drop for LinkedList<T> { fn drop(&mut self) { let mut link = self.0.take(); while let Some(arc_node) = link.take() { if let Some(Node(_value, next)) = Arc::into_inner(arc_node) { link = next; } } } } // `new`や`push`の実装は省略 impl<T> LinkedList<T> { /* ... */ } // 上記`Drop`実装が`Arc::into_inner(arc)`の代わりに`Arc::try_unwrap(arc).ok()`を // 使用していた場合、次のコードではその`Drop`実装でスタックオーバーフローを発生させる可能性がある // 長大なリストを生成して複製 let mut x = LinkedList::new(); for i in 0..100000 { x.push(i); // xの先頭にiを加える } let y = x.clone(); // 並列に複製をドロップする let x_thread = std::thread::spawn(|| drop(x)); let y_thread = std::thread::spawn(|| drop(y)); x_thread.join().unwrap(); y_thread.join().unwrap();
use std::sync::Arc; // `Arc`を使った単純かつ単一なリンクリストの定義 #[derive(Clone)] struct LinkedList<T>(Option<Arc<Node<T>>>); struct Node<T>(T, Option<Arc<Node<T>>>); // `Arc`のデストラクタに依存する長大な`LinkedList<T>`をドロップすると // スタックオーバーフローが発生する可能性がある。これを防ぐため、 // ループで破棄を行う`Drop`を実装する impl<T> Drop for LinkedList<T> { fn drop(&mut self) { let mut link = self.0.take(); while let Some(arc_node) = link.take() { if let Some(Node(_value, next)) = Arc::into_inner(arc_node) { link = next; } } } } // `new`や`push`の実装は省略 impl<T> LinkedList<T> { /* ... */ fn new() -> Self { LinkedList(None) } fn push(&mut self, x: T) { self.0 = Some(Arc::new(Node(x, self.0.take()))); } } // 上記`Drop`実装が`Arc::into_inner(arc)`の代わりに`Arc::try_unwrap(arc).ok()`を // 使用していた場合、次のコードではその`Drop`実装でスタックオーバーフローを発生させる可能性がある // 長大なリストを生成して複製 let mut x = LinkedList::new(); for i in 0..100000 { x.push(i); // xの先頭にiを加える } let y = x.clone(); // 並列に複製をドロップする let x_thread = std::thread::spawn(|| drop(x)); let y_thread = std::thread::spawn(|| drop(y)); x_thread.join().unwrap(); y_thread.join().unwrap();
std::cell::OnceCell
#[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceCell<T> { /* フィールドは省略 */ }
一度だけ書き込むことができるCell。
(Cell
とは異なり)コピーや置換することなく、また(RefCell
とは異なり)実行時の借用検査もなく、
内部値への共有の&T
な参照を得ることができる。ただしCell自体への可変参照がない場合は不変参照だけを得ることができる。
スレッドセーフ版はstd::sync::OnceLock
を参照。
サンプル
use std::cell::OnceCell; let cell = OnceCell::new(); assert!(cell.get().is_none()); let value: &String = cell.get_or_init(|| { "Hello, World!".to_string() }); assert_eq!(value, "Hello, World!"); assert!(cell.get().is_some());
Option::is_some_and
impl<T> Option<T> { #[must_use] #[inline] #[stable(feature = "is_some_and", since = "1.70.0")] pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool { /* 実装は省略 */ } }
このOption
がSome
であり、かつ内部の値が述語に適合する場合にtrue
を返す。
サンプル
let x: Option<u32> = Some(2); assert_eq!(x.is_some_and(|x| x > 1), true); let x: Option<u32> = Some(0); assert_eq!(x.is_some_and(|x| x > 1), false); let x: Option<u32> = None; assert_eq!(x.is_some_and(|x| x > 1), false);
NonNull::slice_from_raw_parts
impl<T> NonNull<[T]> { #[stable(feature = "nonnull_slice_from_raw_parts", since = "1.70.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] #[must_use] #[inline] pub const fn slice_from_raw_parts(data: NonNull<T>, len: usize) -> Self { /* 実装は省略 */ } }
薄いポインタ(thin pointer)と長さから非NULLの生スライスを生成する。
引数len
は要素数でありバイト数ではない。
この関数は安全だが、戻り値を逆参照するのは不安全である。
安全性の要件についてはslice::from_raw_parts
の文書を参照。
サンプル
use std::ptr::NonNull; // 最初の要素へのポインタから始まるスライスポインタを生成する let mut x = [5, 6, 7]; let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); let slice = NonNull::slice_from_raw_parts(nonnull_pointer, 3); assert_eq!(unsafe { slice.as_ref()[2] }, 7);
(注:このサンプルはメソッドの使用法を人為的に示しているに過ぎず、
このような場合ではlet slice = NonNull::from(&x[..]);
といったコードを書く方がより良い)
Result::is_ok_and
impl<T, E> Result<T, E> { #[must_use] #[inline] #[stable(feature = "is_some_and", since = "1.70.0")] pub fn is_ok_and(self, f: impl FnOnce(T) -> bool) -> bool { /* 実装は省略 */ } }
このResult
がOk
であり、かつ内部の値が述語に適合する場合にtrue
を返す。
サンプル
let x: Result<u32, &str> = Ok(2); assert_eq!(x.is_ok_and(|x| x > 1), true); let x: Result<u32, &str> = Ok(0); assert_eq!(x.is_ok_and(|x| x > 1), false); let x: Result<u32, &str> = Err("おはよー"); assert_eq!(x.is_ok_and(|x| x > 1), false);
Result::is_err_and
impl<T, E> Result<T, E> { #[must_use] #[inline] #[stable(feature = "is_some_and", since = "1.70.0")] pub fn is_err_and(self, f: impl FnOnce(E) -> bool) -> bool { /* 実装は省略 */ } }
このResult
がErr
であり、かつ内部の値が述語に適合する場合にtrue
を返す。
サンプル
use std::io::{Error, ErrorKind}; let x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, "!")); assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true); let x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, "!")); assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false); let x: Result<u32, Error> = Ok(123); assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
std::sync::atomic::Atomic*::as_ptr
impl AtomicU8 { #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] pub const fn as_ptr(&self) -> *mut u8 { /* 実装は省略 */ } }
内包する整数への可変ポインタを返す。
戻り値の整数に対して非原子的な読み書き操作を行った場合、データ競合を引き起こす可能性がある。
このメソッドは、&AtomicU8
ではなく*mut u8
を受け取る関数など、専らFFIにおいて便利である。
この原子型への共有参照から*mut
なポインタを返すことは、原子型は内部可変性で動作することから安全である。
原子型でのすべての修正は共有参照を通してその値を変更するものの、これは原子的な操作を使う限りは安全である。
返された生ポインタの使用にはunsafe
ブロックが要求されるが、
同時に「生ポインタへの操作は原子的でなければならない」という制約にも従う必要がある。
サンプル
use std::sync::atomic::AtomicU8; extern "C" { fn my_atomic_op(arg: *mut u8); } let atomic = AtomicU8::new(1); // SAFETY: `my_atomic_op`は原子的である限り安全 unsafe { my_atomic_op(atomic.as_ptr()); }
std::io::IsTerminal
#[stable(feature = "is_terminal", since = "1.70.0")] pub trait IsTerminal: crate::sealed::Sealed { #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; }
記述子・ハンドルが端末・TTYを参照しているかを判断するためのトレイト。
必須のメソッド
fn is_terminal(&self) -> bool;
記述子・ハンドルが端末・TTYを参照している場合にtrue
を返す。
Rustが端末の検出方法を知らないプラットフォームではこのメソッドはfalse
を返す。また、
不正なファイル記述子を与えた場合など予期しないエラーが発生した場合にもfalse
を返す。
プラットフォーム固有の動作
Windowsではコンソールを検出するのに加え、古いmsys・cygwin・mingwなど疑似端末を検出するために
デバイス名を元にしたヒューリスティックな手法(msys-
やcygwin-
で始まり-pty
で終わるデバイス名は端末と見做す)が
使用される。これは将来的に変更される可能性があることに注意されたい。
std::os::linux::net::SocketAddrExt
#[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub trait SocketAddrExt: Sealed { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] fn from_abstract_name<N>(name: N) -> crate::io::Result<SocketAddr> where N: AsRef<[u8]>; #[stable(feature = "unix_socket_abstract", since = "1.70.0")] fn as_abstract_name(&self) -> Option<&[u8]>; }
SocketAddr
へのプラットフォーム固有の拡張。
必須のメソッド
fn from_abstract_name<N>(name: N) -> crate::io::Result<SocketAddr> where N: AsRef<[u8]>;
抽象名前空間はファイルシステムに項目を作成することなくUNIXソケットをバインドする Linux固有の拡張である。抽象ソケットはファイルシステムのレイアウトやパーミッションに 影響されず、ソケットを閉じる際に消去する必要もない。
抽象ソケットアドレス名にはゼロを含む任意のバイトを含められる。
エラー
名前がSUN_LEN - 1
より長い場合はエラーが返される。
サンプル
use std::os::unix::net::{UnixListener, SocketAddr}; use std::os::linux::net::SocketAddrExt; fn main() -> std::io::Result<()> { let addr = SocketAddr::from_abstract_name("秘密".as_bytes())?; let listener = match UnixListener::bind_addr(&addr) { Ok(sock) => sock, Err(err) => { println!("バインドできない:{err:?}"); return Err(err); } }; Ok(()) }
fn as_abstract_name(&self) -> Option<&[u8]>;
このアドレスが抽象名前空間にある場合、その内容を返す。
サンプル
use std::os::unix::net::{UnixListener, SocketAddr}; use std::os::linux::net::SocketAddrExt; fn main() -> std::io::Result<()> { let name = "秘密".as_bytes(); let name_addr = SocketAddr::from_abstract_name(name)?; let socket = UnixListener::bind_addr(&name_addr)?; let local_addr = socket.local_addr().expect("ローカルアドレスを取得できない"); assert_eq!(local_addr.as_abstract_name(), Some(&name[..])); Ok(()) }
std::os::unix::net::UnixDatagram::bind_addr
impl UnixDatagram { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> { /* 実装は省略 */ } }
指定されたパスにUNIXデータグラムソケットを生成する。
サンプル
use std::os::unix::net::{UnixDatagram}; fn main() -> std::io::Result<()> { let sock1 = UnixDatagram::bind("path/to/socket")?; let addr = sock1.local_addr()?; let sock2 = match UnixDatagram::bind_addr(&addr) { Ok(sock) => sock, Err(err) => { println!("バインドできない:{err:?}"); return Err(err); } }; Ok(()) }
std::os::unix::net::UnixDatagram::connect_addr
impl UnixDatagram { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> { /* 実装は省略 */ } }
ソケットをアドレスに接続する。
サンプル
use std::os::unix::net::{UnixDatagram}; fn main() -> std::io::Result<()> { let bound = UnixDatagram::bind("/path/to/socket")?; let addr = bound.local_addr()?; let sock = UnixDatagram::unbound()?; match sock.connect_addr(&addr) { Ok(sock) => sock, Err(e) => { println!("接続できない:{e:?}"); return Err(e) } }; Ok(()) }
std::os::unix::net::UnixDatagram::send_to_addr
impl UnixDatagram { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> { /* 実装は省略 */ } }
指定されたSocketAddrのソケットにデータを送信する。
成功時、書き込まれたバイト数を返す。
サンプル
use std::os::unix::net::{UnixDatagram}; fn main() -> std::io::Result<()> { let bound = UnixDatagram::bind("/path/to/socket")?; let addr = bound.local_addr()?; let sock = UnixDatagram::unbound()?; sock.send_to_addr("目玉焼き".as_bytes(), &addr).expect("send_to_addr関数が失敗"); Ok(()) }
std::os::unix::net::UnixListener::bind_addr
impl UnixListener { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> { /* 実装は省略 */ } }
指定されたソケットアドレスにバインドされるUnixListener
を生成する。
サンプル
use std::os::unix::net::{UnixListener}; fn main() -> std::io::Result<()> { let listener1 = UnixListener::bind("path/to/socket")?; let addr = listener1.local_addr()?; let listener2 = match UnixListener::bind_addr(&addr) { Ok(sock) => sock, Err(err) => { println!("バインドできない:{err:?}"); return Err(err); } }; Ok(()) }
std::path::Path::as_mut_os_str
impl Path { #[stable(feature = "path_as_mut_os_str", since = "1.70.0")] #[must_use] #[inline] pub fn as_mut_os_str(&mut self) -> &mut OsStr { /* 実装は省略 */ } }
内包するOsStr
スライスへの可変参照をもたらす。
サンプル
use std::path::{Path, PathBuf}; let mut path = PathBuf::from("Foo.TXT"); assert_ne!(path, Path::new("foo.txt")); path.as_mut_os_str().make_ascii_lowercase(); assert_eq!(path, Path::new("foo.txt"));
std::sync::OnceLock
#[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock<T> { /* フィールドは省略 */ }
一度だけ書き込むことができる同期プリミティブ。
これはスレッドセーフなOnceCell
であり、静的変数として使用できる。
サンプル
use std::sync::OnceLock; static CELL: OnceLock<String> = OnceLock::new(); assert!(CELL.get().is_none()); std::thread::spawn(|| { let value: &String = CELL.get_or_init(|| { "Hello, World!".to_string() }); assert_eq!(value, "Hello, World!"); }).join().unwrap(); let value: Option<&String> = CELL.get(); assert!(value.is_some()); assert_eq!(value.unwrap().as_str(), "Hello, World!");
変更点リスト
言語
asm!
のオペランドにおける順序規則を緩和- 展開されたマクロでの
format_args
の呼び出しでキャプチャの使用を適切に許容 - グロブによる曖昧な再エクスポートへのリント
let _ = expr
の位置にある式でconstとunsafeの検査を実施
コンパイラ
- 新しいオプションとその別名により-Cdebuginfoを拡張。
これは行番号の情報のみを必要とする場合向けにデバッグ情報の小型版(
-Cdebuginfo=line-tables-only
)を提供するもので、 いずれ-Cdebuginfo=1
の既定値となる可能性がある unused_allocation
リントをBox::new
にも適用- 定数評価の早い段階で無人型(uninhabited types=バリアントのない列挙型)を検知
- {arm,thumb}v4t-none-eabiで既定のリンカーをLLDに切り替え
loongarch64-unknown-linux-gnu
をTier 3ターゲットとして追加i586-pc-nto-qnx700
(QNX Neutrino RTOSバージョン7.0)をTier 3ターゲットとして追加,- ポインタ逆参照にアライメント検査をデバッグ時表明として挿入。 これは実行時に未定義動作を捉えるもので、既存コードを失敗させる可能性がある
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
- NonZeroXxxの割り付けに関する保証を文書化
- Windows:
Command
で非逐語的なパスを優先 - alloc・coreにある一部のイテレータがDefaultを実装するようになった
- str::linesにおける末尾のむき出しなCRの処理を修正
concat!
において負の数値リテラルを許容Cell
のメモリ割り付けに関する文書を追加- タプルでの
lt
・le
・ge
・gt
の実装にpartial_cmp
を使用 atomic_as_ptr
を安定化nonnull_slice_from_raw_parts
を安定化once_cell
を部分的に安定化nonzero_min_max
を安定化- format_args!()と(文字列・数値)リテラル引数をformat_args!()内に平坦化・インライン化
- movbeターゲット機能を安定化
- io::copyでファイルからパイプに継ぎ足さない
- すべての関数ポインタに実装される、不安定な組み込みトレイト
FnPtr
を追加。 これは全ABIの関数ポインタでDebug
、Pointer
、Hash
、PartialEq
、Eq
、PartialOrd
、そしてOrd
の実装を拡張する (※訳注:引数の数に関係なくこれらのトレイトが実装されるようになる)
安定化されたAPI
NonZero*::MIN/MAX
BinaryHeap::retain
Default for std::collections::binary_heap::IntoIter
Default for std::collections::btree_map::{IntoIter, Iter, IterMut}
Default for std::collections::btree_map::{IntoKeys, Keys}
Default for std::collections::btree_map::{IntoValues, Values}
Default for std::collections::btree_map::Range
Default for std::collections::btree_set::{IntoIter, Iter}
Default for std::collections::btree_set::Range
Default for std::collections::linked_list::{IntoIter, Iter, IterMut}
Default for std::vec::IntoIter
Default for std::iter::Chain
Default for std::iter::Cloned
Default for std::iter::Copied
Default for std::iter::Enumerate
Default for std::iter::Flatten
Default for std::iter::Fuse
Default for std::iter::Rev
Default for std::slice::Iter
Default for std::slice::IterMut
Rc::into_inner
Arc::into_inner
std::cell::OnceCell
Option::is_some_and
NonNull::slice_from_raw_parts
Result::is_ok_and
Result::is_err_and
std::sync::atomic::Atomic*::as_ptr
std::io::IsTerminal
std::os::linux::net::SocketAddrExt
std::os::unix::net::UnixDatagram::bind_addr
std::os::unix::net::UnixDatagram::connect_addr
std::os::unix::net::UnixDatagram::send_to_addr
std::os::unix::net::UnixListener::bind_addr
std::path::Path::as_mut_os_str
std::sync::OnceLock
Cargo
CARGO_PKG_README
を追加- crates.io向けの既定プロトコルを
sparse
にする - 依存クレートのダウングレード時、ステータスを正確に表示
- ログイン・ログアウト時にregistry.defaultを使用
cargo logout
を安定化
その他
互換性メモ
- 安定版の
libtest
が-Zunstable-options
に対応するのを阻止 let _ = expr
の位置にある式でconstとunsafeの検査を実施- WebAssemblyターゲットにおいて、コード生成の機能
sign-ext
とmutable-globals
を有効化。 これにより古い実行環境との互換性が失われる可能性がある - ポインタ逆参照にアライメント検査をデバッグ時表明として挿入。 これは実行時に未定義動作を捉えるもので、既存コードを失敗させる可能性がある
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。
関連リンク
さいごに
次のリリースのRust 1.71は7/14(金)にリリースされる予定です。 Rust 1.71ではタプルと配列を相互に変換できるようになるようです。