本日1/10(金)にリリースされたRust 1.84の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。
- ピックアップ
- 安定化されたAPIのドキュメント
- Ipv6Addr::is_unique_local
- Ipv6Addr::is_unicast_link_local
- core::ptr::with_exposed_provenance
- core::ptr::with_exposed_provenance_mut
- <ptr>::addr
- <ptr>::expose_provenance
- <ptr>::with_addr
- <ptr>::map_addr
- <int>::isqrt
- <int>::checked_isqrt
- <uint>::isqrt
- NonZero::isqrt
- core::ptr::without_provenance
- core::ptr::without_provenance_mut
- core::ptr::dangling
- core::ptr::dangling_mut
- Pin::as_deref_mut
- 変更点リスト
- 関連リンク
- さいごに
- ライセンス表記
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
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"の設定が既定となり、何もしなくてもこの動作が有効になる予定です。
トレイト解決の仕組み更新が始まった
トレイトを解決する仕組みの一部が入れ替わり、一貫性が向上したらしいです。 ごくまれに正しいコードがコンパイルできない問題があったようですが、それが解消されたとのことです。
この新しい仕組みについてはTaKO8Kiさんの解説記事があるので、こちらも参考にしてください。
target tripleをtarget tupleと呼ぶようになった
これまでプラットフォームを区別する文字列はtarget tripleと呼ばれていました。
ただしその名前とは裏腹に、実際に3つの要素を持つものはaarch64-apple-darwinなどティア1ではApple系のもののみで、
Linuxはx86_64-unknown-linux-gnu、Windowsはx86_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);
Ipv6Addr::is_unicast_link_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_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 { /* 実装は省略 */ }
アドレスをポインタに変換し、以前に「公開」されていた由来を選択する。
これはaddr as *const Tと完全に等価である。返されるポインタの由来は、
以前にexpose_provenanceへの引き渡し、またはptr as usizeでのキャストによって
公開されたものの*いずれかである。また、Rust抽象マシンの制御外にあるメモリ(MMIOレジスタなど)は、
スタック、ヒープ、静的変数などの抽象マシンによって使用されるメモリから分離されている限り、
公開された由来でアクセス可能であると常に見なされる。
返されるポインタがどの由来を選択するかは明確に指定されていない。 コンパイラは(どれが正しいかに関わらず)できるだけ最適な由来を選択するが、 現時点では返されるポインタがどこに由来するかについては保証ができない。 したがって、返されるポインタがアクセスできるメモリについての明確な仕様はない。
返されるポインタの使用方法を正当化する以前に「公開」された由来がない場合、 プログラムは未定義の動作を起こす。特に、別名付け規則は引き続き適用される。 別名アクセスによって無効化されたポインタや参照は、それらが公開されていても使用できない。
この操作は本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。
可能な限りwith_addrなどの厳密な由来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 { /* 実装は省略 */ }
アドレスを可変ポインタに変換し、以前に「公開」されていた由来を選択する。
これはaddr as *mut Tと完全に等価である。返されるポインタの由来は、
以前にexpose_provenanceへの引き渡し、またはptr as usizeでのキャストによって
公開されたものの*いずれかである。また、Rust抽象マシンの制御外にあるメモリ(MMIOレジスタなど)は、
スタック、ヒープ、静的変数などの抽象マシンによって使用されるメモリから分離されている限り、
公開された由来でアクセス可能であると常に見なされる。
返されるポインタがどの由来を選択するかは明確に指定されていない。 コンパイラは(どれが正しいかに関わらず)できるだけ最適な由来を選択するが、 現時点では返されるポインタがどこに由来するかについては保証ができない。 したがって、返されるポインタがアクセスできるメモリについての明確な仕様はない。
返されるポインタの使用方法を正当化する以前に「公開」された由来がない場合、 プログラムは未定義の動作を起こす。特に、別名付け規則は引き続き適用される。 別名アクセスによって無効化されたポインタや参照は、それらが公開されていても使用できない。
この操作は本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。
可能な限りwith_addrなどの厳密な由来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と似ているものの、ポインタの由来は破棄されるが公開された由来は破棄されない。
返されたアドレスをポインタに戻すと由来のないポインタが得られるためであり、
これを逆参照することは未定義の動作である。失われた情報を適切に復元して逆参照可能なポインタを得るには、
with_addrまたはmap_addrを使用する。
必須の由来付きポインタを保持する方法がないためにこれらのAPIを使用できない場合、
厳密な由来が適していない可能性がある。代わりにポインタから整数へのキャスト、
またはexpose_provenanceとwith_exposed_provenanceを
使用されたい。ただし、これによりコードの移植性が低下し、Rustメモリモデルへの準拠を
チェックするツールに適さなくなることにも注意されたい。
ほとんどのプラットフォームではバイト列全体でアドレスを記述するため、 元のポインタと同じバイト数の値が生成される。 ポインタに追加情報を格納する必要があるプラットフォームは、 ポインタのアドレス部分のみ含む値を生成するために表現を変更する可能性がある。 その意味はプラットフォームによって定義される。
<ptr>::expose_provenance
#[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] pub fn expose_provenance(self) -> usize { /* 実装は省略 */ } }
今後with_exposed_provenanceで使用するため、
ポインタの由来部を公開して「アドレス」部分を返す。
これはself as usizeと等価で、意味論的に由来情報を破棄する。
さらに、これは(asでのキャストと同様に)由来を「公開」として記録する暗黙の副作用があるため、
これに対応するプラットフォームでは、
後でwith_exposed_provenanceを呼び出すことでその由来を含む元のポインタを再構成することができる。
with_exposed_provenanceは本質的に曖昧であるため、Rustメモリモデルに準拠するツールではこの操作に対応しない場合がある。
可能な限りwith_addrなどの厳密な由来APIの使用を推奨する。
この場合、expose_provenanceの代わりにaddrを使用するべきである。
ほとんどのプラットフォームではバイト列全体でアドレスを記述するため、
元のポインタと同じバイト数の値が生成される。
ポインタに追加情報を格納する必要があるプラットフォームは、
with_exposed_provenanceの動作に必要な「公開」の副作用を一般的には利用できないため、
この操作に対応しないかもしれない。
<ptr>::with_addr
#[must_use] #[inline] #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn with_addr(self, addr: usize) -> Self { /* 実装は省略 */ } }
指定されたアドレスとselfの由来を持つ新しいポインタを作成する。
これはaddr as *const Tと似ているものの、selfの由来を新しいポインタにコピーする。
これにより単項キャスト固有の曖昧さを回避する。
これはwrapping_offsetを使用してselfを指定されたアドレスにずらすことと等価であり、
したがってすべての同じ機能と制限を持つ。
<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の由来を保持しながらselfのアドレスを新しいアドレスに射影し、
新しいポインタを作成する。
これはwith_addrへの近道であり、詳細はそちらを参照。
<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 { /* 実装は省略 */ }
指定されたアドレスで由来のないポインタを作成する。
これはptr::null().with_addr(addr)と等価である。
由来がないため、このポインタは実際の割り当てとは無関係である。
このような由来のないポインタは(適切に整列されている場合)ゼロサイズのメモリアクセスに使用できるが、
由来のないポインタを使用して非ゼロサイズのメモリアクセスを行うと未定義の動作となる。
由来のないポインタは変装したusizeなアドレスのようなものである。
これはaddr as *const Tとは異なり、以前に公開された由来を拾うポインタを作成する。
詳細はwith_exposed_provenanceを参照。
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 { /* 実装は省略 */ }
指定されたアドレスで由来のないポインタを作成する。
これはptr::null_mut().with_addr(addr)と等価である。
由来がないため、このポインタは実際の割り当てとは無関係である。
このような由来のないポインタは(適切に整列されている場合)ゼロサイズのメモリアクセスに使用できるが、
由来のないポインタを使用して非ゼロサイズのメモリアクセスを行うと未定義の動作となる。
由来のないポインタは変装したusizeなアドレスのようなものである。
これはaddr as *mut Tとは異なり、以前に公開された由来を拾うポインタを作成する。
詳細は[with_exposed_provenance_mut]を参照。
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の契約により排除される。
変更点リスト
言語
#[forbid]内での#[deny]を無操作として許可- ABIの不一致により不良動作(unsoundness)を引き起こす可能性のある機能を切り替えるために
-Ctarget-featureを使用した場合に警告を表示 - 一貫性(coherence)のため次世代のトレイト解決機構を使用
- トレイトオブジェクトの大元(principal)をドロップするための型強制を許容
- Windowsの全場面において、
include!()のパス区切りとして/に対応 - ポインタへの逆参照に対する生参照(
raw (const|mut))の取得が安全になった - s390xインラインアセンブリの安定化
- Arm64ECインラインアセンブリの安定化
- 即時にドロップする一時変数へのポインタ作成に対して警告
extern "C"関数内での巻き戻し時に解放用自動生成コード(drop glue)を実行
コンパイラ
- ホストのtarget tripleを表示する
--print host-tupleフラグを追加し、用語も「target triple」ではなく「target tuple」を使用するようにした - 現在のターゲットが非対応の呼び出し規約で関数を宣言するとコンパイルエラーが発生するようになった
loongarch64-unknown-linux-{musl,ohos}向けに外部データへの間接アクセスを設定- LoongArch Linuxターゲット向けのXRay計装を有効化
- リント
unexpected_cfgsが外部マクロ内でも警告するよう拡張 - WebAssemblyの
multivalue・reference-types・tail-callターゲット機能を安定化 - ティア2の
wasm32v1-noneターゲットを追加
Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照
ライブラリ
impl From<&mut {slice}> for Box/Rc/Arc<{slice}><float>::copysign・<float>::abs・<float>::signumをcoreに移動NonZeroがLowerExpとUpperExpを実装するようになったimpl FromStr for CStringとimpl TryFrom<CString> for Stringを実装std::os::darwinを公開
安定化されたAPI
Ipv6Addr::is_unique_localIpv6Addr::is_unicast_link_localcore::ptr::with_exposed_provenancecore::ptr::with_exposed_provenance_mut<ptr>::addr<ptr>::expose_provenance<ptr>::with_addr<ptr>::map_addr<int>::isqrt<int>::checked_isqrt<uint>::isqrtNonZero::isqrtcore::ptr::without_provenancecore::ptr::without_provenance_mutcore::ptr::danglingcore::ptr::dangling_mut
以下のAPIが定数文脈で使えるようになった。
AtomicBool::from_ptrAtomicPtr::from_ptrAtomicU8::from_ptrAtomicU16::from_ptrAtomicU32::from_ptrAtomicU64::from_ptrAtomicUsize::from_ptrAtomicI8::from_ptrAtomicI16::from_ptrAtomicI32::from_ptrAtomicI64::from_ptrAtomicIsize::from_ptr<ptr>::is_null<ptr>::as_ref<ptr>::as_mutPin::newPin::new_uncheckedPin::get_refPin::into_refPin::get_mutPin::get_unchecked_mutPin::static_refPin::static_mut
Cargo
Rustdoc
互換性メモ
- LoongArch Linuxターゲット向けの
LSXターゲット機能を既定で有効化 - 不安定だった
-Zprofileフラグ(gcovスタイルのカバレッジ計装)が削除された。これは安定版のカバレッジ計装(-Cinstrument-coverage)とPGO(-Cprofile-generate、-Cprofile-use)には影響しない。これらは無関係であり引き続き利用可能である wasm32-wasip1に名称変更されているため、wasm32-wasiターゲットへの対応が削除された。これによりRust 1.78で導入されたwasm32-wasip1に続く移行 計画が完了した。ターゲットとともに、Rust 1.81で導入されたwasm32-wasiの使用に関するコンパイラ警告も削除された&pin (mut|const) Tの構文が、理論的には一部のエッジケースでマクロ展開の結果に影響を与える可能性がある型として解析されるようになったstd::arch関数を呼び出すための従来の構文においては、アイテムや本体(クロージャ、インライン定数、非同期ブロックなど)を宣言できなくなった- 現在のターゲットが非対応の呼び出し規約で関数を宣言するとコンパイルエラーが発生するようになった
- 一貫性(coherence)のために次世代のトレイト解決機構が有効化され、多くの健全性(soundness)に関する問題が修正された
関連リンク
さいごに
次のリリースのRust 1.85は2/20(金)にリリースされる予定です。 Rust 1.85では皆さんお待ちかね、Rust 2024エディションが使えるようになる予定です。