以下の内容はhttps://aznhe21.hatenablog.com/entry/2025/01/10/rust-1.84より取得しました。


Rust 1.84を早めに深掘り

本日1/10(金)にリリースされたRust 1.84の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。

ピックアップ

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

Rustの最小バージョン(MSRV)を考慮した依存解決ができるようになった

現時点では手動で設定する必要があるものの、Cargo.tomlでpackage.rust-version(MSRV)を設定した場合、 間接的に依存しているクレートのバージョン選択時にMSRVが考慮されるようになりました。

これまではMSRVを設定したとき、cargo addで直接追加するクレートはMSRVが考慮されていましたが、 そのクレート経由で依存しているクレートはMSRVが考慮されませんでした。 cargo addではなく手動で指定した場合も同様です。

Rust 1.84からは.cargo/config.tomlまたはCargo.tomlで次のように設定することで、 クレートのバージョン選択時にMSRVが考慮されるようになります。

# .cargo/config.tomlで設定する場合
[resolver]
incompatible-rust-versions = "fallback"
# 環境変数CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONSでも設定可能
# Cargo.tomlで設定する場合
[package]
resolver = "3"

なおRust 2024エディションではpackage.resolver = "3"の設定が既定となり、何もしなくてもこの動作が有効になる予定です。

MSRVを考慮したクレート選択(左)と考慮しない選択(右)
MSRVを考慮したクレート選択(左)と考慮しない選択(右)を表示した様子

トレイト解決の仕組み更新が始まった

トレイトを解決する仕組みの一部が入れ替わり、一貫性が向上したらしいです。 ごくまれに正しいコードがコンパイルできない問題があったようですが、それが解消されたとのことです。

この新しい仕組みについてはTaKO8Kiさんの解説記事があるので、こちらも参考にしてください。

target tripleをtarget tupleと呼ぶようになった

これまでプラットフォームを区別する文字列はtarget tripleと呼ばれていました。 ただしその名前とは裏腹に、実際に3つの要素を持つものはaarch64-apple-darwinなどティア1ではApple系のもののみで、 Linuxx86_64-unknown-linux-gnuWindowsx86_64-pc-windows-msvcと4要素だったり、 ティア3ですがWASIはwasm32-wasip2と2要素だったりと一貫性がありませんでした。

Rust 1.84ではrustc --print host-tupleといったコマンドが追加されるなど、tripleではなくtupleと呼ぶことになりました。

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

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

Ipv6Addr::is_unique_local

原典

    #[must_use]
    #[inline]
    #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")]
    #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")]
    pub const fn is_unique_local(&self) -> bool
    { /* 実装は省略 */ }
}

ユニークローカルアドレスfc00::/7)である場合にtrueを返す。

この特性はIETFのRFC 4193で定義されている。

サンプル

use std::net::Ipv6Addr;

assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false);
assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true);

原典

    #[must_use]
    #[inline]
    #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")]
    #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")]
    pub const fn is_unicast_link_local(&self) -> bool
    { /* 実装は省略 */ }
}

RFC 4291で定義されているように、ユニキャストアドレスのスコープがリンクローカルである場合にtrueを返す。

RFC 4291セクション2.4によると、ユニキャストアドレスがfe80::/10で始まる場合はリンクローカルスコープである。 これはRFC 4291セクション2.5.6で定義されている「リンクローカルなIPv6ユニキャストアドレス」の形式よりも広い範囲を含む。

| 10ビット |        54ビット         |          64ビット          |
+----------+-------------------------+----------------------------+
|1111111010|           0             |       interface ID         |
+----------+-------------------------+----------------------------+

従って、現在はアプリケーションが遭遇するリンクローカルスコープのアドレスはすべてfe80::/64にしかないが、 将来的には新しい標準の発行により変わる可能性がある。fe80::/10にさらに多くのアドレスが割り当てられた場合、 そのアドレスはリンクローカルスコープとなることになる。

またRFC 4291セクション2.5.3ではループバックアドレス::1)が「リンクローカルスコープとして扱われる」と言及されいるが、 これはループバックアドレスが実際にリンクローカルスコープであるわけではなく、このメソッドはfalseを返す。

サンプル

use std::net::Ipv6Addr;

// ループバックアドレス(`::1`)のスコープは実際にはリンクローカルではない
assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false);

// `fe80::/10`内のアドレスのみがリンクローカルスコープである
assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false);
assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);

// より厳密な`fe80::/64`外のアドレスのスコープもリンクローカルである
assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true);
assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);

core::ptr::with_exposed_provenance

原典

#[must_use]
#[inline(always)]
#[stable(feature = "exposed_provenance", since = "1.84.0")]
pub fn with_exposed_provenance<T>(addr: usize) -> *const T
{ /* 実装は省略 */ }

アドレスをポインタに変換し、以前に「公開」されていた由来provenanceを選択する。

これはaddr as *const Tと完全に等価である。返されるポインタの由来は、 以前にexpose_provenanceへの引き渡し、またはptr as usizeでのキャストによって 公開されたものの*いずれかである。また、Rust抽象マシンの制御外にあるメモリ(MMIOレジスタなど)は、 スタック、ヒープ、静的変数などの抽象マシンによって使用されるメモリから分離されている限り、 公開された由来でアクセス可能であると常に見なされる。

返されるポインタがどの由来を選択するかは明確に指定されていない。 コンパイラは(どれが正しいかに関わらず)できるだけ最適な由来を選択するが、 現時点では返されるポインタがどこに由来するかについては保証ができない。 したがって、返されるポインタがアクセスできるメモリについての明確な仕様はない。

返されるポインタの使用方法を正当化する以前に「公開」された由来がない場合、 プログラムは未定義の動作を起こす。特に、別名付け規則aliasing ruleは引き続き適用される。 別名アクセスによって無効化されたポインタや参照は、それらが公開されていても使用できない。

この操作は本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。 可能な限りwith_addrなどの厳密な由来APIの使用を推奨する。

ほとんどのプラットフォームではアドレスと同じバイト数の値が生成される。 ポインタに追加情報を格納する必要があるプラットフォームは、 返される値がどの由来を選択するかを実際に計算することは一般的に不可能なため、 この操作に対応しないかもしれない。

このAPI公開された由来APIである。

core::ptr::with_exposed_provenance_mut

原典

#[must_use]
#[inline(always)]
#[stable(feature = "exposed_provenance", since = "1.84.0")]
pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T
{ /* 実装は省略 */ }

アドレスを可変ポインタに変換し、以前に「公開」されていた由来provenanceを選択する。

これはaddr as *mut Tと完全に等価である。返されるポインタの由来は、 以前にexpose_provenanceへの引き渡し、またはptr as usizeでのキャストによって 公開されたものの*いずれかである。また、Rust抽象マシンの制御外にあるメモリ(MMIOレジスタなど)は、 スタック、ヒープ、静的変数などの抽象マシンによって使用されるメモリから分離されている限り、 公開された由来でアクセス可能であると常に見なされる。

返されるポインタがどの由来を選択するかは明確に指定されていない。 コンパイラは(どれが正しいかに関わらず)できるだけ最適な由来を選択するが、 現時点では返されるポインタがどこに由来するかについては保証ができない。 したがって、返されるポインタがアクセスできるメモリについての明確な仕様はない。

返されるポインタの使用方法を正当化する以前に「公開」された由来がない場合、 プログラムは未定義の動作を起こす。特に、別名付け規則aliasing ruleは引き続き適用される。 別名アクセスによって無効化されたポインタや参照は、それらが公開されていても使用できない。

この操作は本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。 可能な限りwith_addrなどの厳密な由来APIの使用を推奨する。

ほとんどのプラットフォームではアドレスと同じバイト数の値が生成される。 ポインタに追加情報を格納する必要があるプラットフォームは、 返される値がどの由来を選択するかを実際に計算することは一般的に不可能なため、 この操作に対応しないかもしれない。

このAPI公開された由来APIである。

<ptr>::addr

原典

impl<T: ?Sized> *const T {
    #[must_use]
    #[inline(always)]
    #[stable(feature = "strict_provenance", since = "1.84.0")]
    pub fn addr(self) -> usize
    { /* 実装は省略 */ }
}

ポインタの「アドレス」部分を取得する。

これはself as usizeと似ているものの、ポインタの由来provenanceは破棄されるが公開された由来は破棄されない。 返されたアドレスをポインタに戻すと由来のないポインタが得られるためであり、 これを逆参照することは未定義の動作である。失われた情報を適切に復元して逆参照可能なポインタを得るには、 with_addrまたはmap_addrを使用する。

必須の由来付きポインタを保持する方法がないためにこれらのAPIを使用できない場合、 厳密な由来が適していない可能性がある。代わりにポインタから整数へのキャスト、 またはexpose_provenancewith_exposed_provenanceを 使用されたい。ただし、これによりコードの移植性が低下し、Rustメモリモデルへの準拠を チェックするツールに適さなくなることにも注意されたい。

ほとんどのプラットフォームではバイト列全体でアドレスを記述するため、 元のポインタと同じバイト数の値が生成される。 ポインタに追加情報を格納する必要があるプラットフォームは、 ポインタのアドレス部分のみ含む値を生成するために表現を変更する可能性がある。 その意味はプラットフォームによって定義される。

このAPI厳密な由来APIである。

<ptr>::expose_provenance

原典

    #[must_use]
    #[inline(always)]
    #[stable(feature = "exposed_provenance", since = "1.84.0")]
    pub fn expose_provenance(self) -> usize
    { /* 実装は省略 */ }
}

今後with_exposed_provenanceで使用するため、 ポインタの由来provenance部を公開して「アドレス」部分を返す。

これはself as usizeと等価で、意味論的に由来情報を破棄する。 さらに、これは(asでのキャストと同様に)由来を「公開」として記録する暗黙の副作用があるため、 これに対応するプラットフォームでは、 後でwith_exposed_provenanceを呼び出すことでその由来を含む元のポインタを再構成することができる。

with_exposed_provenanceは本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。 可能な限りwith_addrなどの厳密な由来APIの使用を推奨する。 この場合、expose_provenanceの代わりにaddrを使用するべきである。

ほとんどのプラットフォームではバイト列全体でアドレスを記述するため、 元のポインタと同じバイト数の値が生成される。 ポインタに追加情報を格納する必要があるプラットフォームは、 with_exposed_provenanceの動作に必要な「公開」の副作用を一般的には利用できないため、 この操作に対応しないかもしれない。

このAPI公開された由来APIである。

<ptr>::with_addr

原典

    #[must_use]
    #[inline]
    #[stable(feature = "strict_provenance", since = "1.84.0")]
    pub fn with_addr(self, addr: usize) -> Self
    { /* 実装は省略 */ }
}

指定されたアドレスとself由来provenanceを持つ新しいポインタを作成する。

これはaddr as *const Tと似ているものの、self由来を新しいポインタにコピーする。 これにより単項キャスト固有の曖昧さを回避する。

これはwrapping_offsetを使用してselfを指定されたアドレスにずらすことと等価であり、 したがってすべての同じ機能と制限を持つ。

このAPI厳密な由来APIである。

<ptr>::map_addr

原典

    #[must_use]
    #[inline]
    #[stable(feature = "strict_provenance", since = "1.84.0")]
    pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
    { /* 実装は省略 */ }
}

self由来provenanceを保持しながらselfのアドレスを新しいアドレスに射影し、 新しいポインタを作成する。

これはwith_addrへの近道であり、詳細はそちらを参照。

このAPI厳密な由来APIである。

<int>::isqrt

原典

impl i32 {
    #[stable(feature = "isqrt", since = "1.84.0")]
    #[rustc_const_stable(feature = "isqrt", since = "1.84.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline]
    #[track_caller]
    pub const fn isqrt(self) -> Self
    { /* 実装は省略 */ }
}

数値の平方根を切り捨てて返す。

パニック

selfが負の場合、この関数はパニックする。

サンプル

基本的な使い方。

assert_eq!(10i32.isqrt(), 3);

<int>::checked_isqrt

原典

impl i32 {
    #[stable(feature = "isqrt", since = "1.84.0")]
    #[rustc_const_stable(feature = "isqrt", since = "1.84.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline]
    pub const fn checked_isqrt(self) -> Option<Self>
    { /* 実装は省略 */ }
}

数値の平方根を切り捨てて返す。

selfが負の場合はNoneを返す。

サンプル

基本的な使い方。

assert_eq!(10i32.checked_isqrt(), Some(3));

<uint>::isqrt

原典

impl u32 {
    #[stable(feature = "isqrt", since = "1.84.0")]
    #[rustc_const_stable(feature = "isqrt", since = "1.84.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline]
    pub const fn isqrt(self) -> Self
    { /* 実装は省略 */ }
}

数値の平方根を切り捨てて返す。

サンプル

基本的な使い方。

assert_eq!(10u32.isqrt(), 3);

NonZero::isqrt

原典

    #[stable(feature = "isqrt", since = "1.84.0")]
    #[rustc_const_stable(feature = "isqrt", since = "1.84.0")]
    #[must_use = "this returns the result of the operation, \
                  without modifying the original"]
    #[inline]
    pub const fn isqrt(self) -> Self
    { /* 実装は省略 */ }
}

数値の平方根を切り捨てて返す。

サンプル

基本的な使い方。

let ten = NonZero::new(10u128)?;
let three = NonZero::new(3u128)?;

assert_eq!(ten.isqrt(), three);
use std::num::NonZero;

fn main() { test().unwrap(); }
fn test() -> Option<()> {
    let ten = NonZero::new(10u128)?;
    let three = NonZero::new(3u128)?;

    assert_eq!(ten.isqrt(), three);
    Some(())
}

core::ptr::without_provenance

原典

#[inline(always)]
#[must_use]
#[stable(feature = "strict_provenance", since = "1.84.0")]
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
pub const fn without_provenance<T>(addr: usize) -> *const T
{ /* 実装は省略 */ }

指定されたアドレスで由来provenanceのないポインタを作成する。

これはptr::null().with_addr(addr)と等価である。

由来がないため、このポインタは実際の割り当てとは無関係である。 このような由来のないポインタは(適切に整列されている場合)ゼロサイズのメモリアクセスに使用できるが、 由来のないポインタを使用して非ゼロサイズのメモリアクセスを行うと未定義の動作となる。 由来のないポインタは変装したusizeなアドレスのようなものである。

これはaddr as *const Tとは異なり、以前に公開された由来を拾うポインタを作成する。 詳細はwith_exposed_provenanceを参照。

このAPI厳密な由来APIである。

core::ptr::without_provenance_mut

原典

#[inline(always)]
#[must_use]
#[stable(feature = "strict_provenance", since = "1.84.0")]
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T
{ /* 実装は省略 */ }

指定されたアドレスで由来provenanceのないポインタを作成する。

これはptr::null_mut().with_addr(addr)と等価である。

由来がないため、このポインタは実際の割り当てとは無関係である。 このような由来のないポインタは(適切に整列されている場合)ゼロサイズのメモリアクセスに使用できるが、 由来のないポインタを使用して非ゼロサイズのメモリアクセスを行うと未定義の動作となる。 由来のないポインタは変装したusizeなアドレスのようなものである。

これはaddr as *mut Tとは異なり、以前に公開された由来を拾うポインタを作成する。 詳細は[with_exposed_provenance_mut]を参照。

このAPI厳密な由来APIである。

core::ptr::dangling

原典

#[inline(always)]
#[must_use]
#[stable(feature = "strict_provenance", since = "1.84.0")]
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
pub const fn dangling<T>() -> *const T
{ /* 実装は省略 */ }

danglingであるが、非nullで整列された新しいポインタを作成する。

これはVec::newのように遅延割り当てを行う型での初期化に有用である。

このポインタの値は潜在的Tへの有効なポインタを表す可能性があることに注意されたい。 つまり、これを「未初期化」の番兵として使用してはならない。 遅延割り当てを行う型は他の方法で初期化を追跡する必要がある。

core::ptr::dangling_mut

原典

#[inline(always)]
#[must_use]
#[stable(feature = "strict_provenance", since = "1.84.0")]
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
pub const fn dangling_mut<T>() -> *mut T
{ /* 実装は省略 */ }

danglingであるが、非nullで整列された新しいポインタを作成する。

これはVec::newのように遅延割り当てを行う型での初期化に有用である。

このポインタの値は潜在的Tへの有効なポインタを表す可能性があることに注意されたい。 つまり、これを「未初期化」の番兵として使用してはならない。 遅延割り当てを行う型は他の方法で初期化を追跡する必要がある。

Pin::as_deref_mut

原典

impl<Ptr: DerefMut> Pin<Ptr> {
    #[stable(feature = "pin_deref_mut", since = "1.84.0")]
    #[must_use = "`self` will be dropped if the result is not used"]
    #[inline(always)]
    pub fn as_deref_mut(self: Pin<&mut Pin<Ptr>>) -> Pin<&mut Ptr::Target>
    { /* 実装は省略 */ }
}

入れ子になったPinポインタから、内包するピン留めされた値へのPin<&mut T>を取得する。

このメソッドはPin<&mut Pin<Pointer<T>>>からPin<&mut T>を得るための一般的なメソッドである。 Pin<Pointer<T>>が存在することで参照先Tが将来移動しないことが保証され、 またこのメソッドにより参照先を移動させることはないため、このメソッドは安全である。 Ptr::DerefMutの「悪意のある」実装もPin::new_uncheckedの契約により排除される。

変更点リスト

言語

コンパイラ

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

ライブラリ

安定化されたAPI

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

Cargo

Rustdoc

互換性メモ

関連リンク

さいごに

次のリリースのRust 1.85は2/20(金)にリリースされる予定です。 Rust 1.85では皆さんお待ちかね、Rust 2024エディションが使えるようになる予定です。

ライセンス表記

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



以上の内容はhttps://aznhe21.hatenablog.com/entry/2025/01/10/rust-1.84より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14