never型フォールバックの変更
Rust 2024では、never型(!)から任意の型への変換時のフォールバック先が、ユニット型(())でなくnever型(!)になりました。
never型(!)とは、loop式のように終了せず値を生成しない繰り返し式や、panic!()などの発散関数の戻り値を表す型です。処理が到達しない、値を生成しないことを表す抽象的なデータ型の一つです(ボトム型とも呼ばれます)。
関数が無限ループやpanic!()のみを含む場合、その関数からは戻ってきません。値を明示的に返さない関数はユニット(())を返すことになっていますが、こういった場合と区別するためにあるのがnever型(!)です。このnever型は、以下のリストのように別の型に型強制できます。これは、match式の各アームは同じ型を生成する必要があるので、最後のpanic!()もbool型となる必要があるからです。
let x = Some(1_u8);
let a = match x {
Some(_) => true, // このアームはbool
_ => panic!(), // ! → boolに型強制される
};
型強制ではなく、以下のリストのように型推論によって別の型に変換したいとき、型推論がうまくいかない場合にはフォールバックされます。これは、「never型フォールバック」と呼ばれます。従来は、フォールバック先はユニット型(())でした。しかしながら、この変換は時として混乱を招くものでした。
let v = panic!(); // 2024では!、従来は()
Rust 2024では、never型フォールバックにおいて、そのままnever型(!)が使われます。現時点ではRust 2024に対してのみの適用ですが、過去エディションにも適用される計画があるようです。この変更で、never型を含む式のより直感的な記述が可能になります。
RPITのライフタイムキャプチャ方法の改善
Rust 2024では、RPIT(戻り位置のImpl Trait)のライフタイムのキャプチャ方法が改善されました。
RPIT(戻り位置のImpl Trait)とは、メソッドの戻り値の型を表す場所にimpl Traitを記述することです(Traitはトレイトの名前)。これは以下のように、メソッドの戻り値がTraitトレイトを実装している型であることを示すための記法です。
fn func() -> impl Trait {
...Traitを実装する型の値を返す...
}
実際に返される型はコンパイル時に決定する必要があるので、impl Traitはあくまでも実際の型を隠す型として、Opaque type(覆い隠す型)と呼ばれます。これに対して実際の型はOpaque typeに隠される型として、Hidden type(隠される型)と呼ばれます。
Opaque typeには、'aなどのライフタイムを含むことができず、実際のライフタイムはHidden typeからキャプチャされます。このキャプチャの方法が、非同期関数と同期関数で異なっていることから、非同期関数の方法に合わせることが、この改善の目的です。
この方法とは、impl Traitのトレイト境界に、Rust 1.82で導入されたuse境界を追加するものです(非同期関数においてasync fnがimpl Futureに展開されるときに追加される)。
以下のリストは、use境界を含む関数定義の例となっています。これにより、Opaque typeであるimpl Sizedにライフタイム'aが明示的に指定できます。
fn func<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> {
(x, y)
}
Rust 2024では、use境界も不要になります。つまり、以下のリストのようにすっきりとRPITを記述できます(use境界を用いた記述も引き続き有効です)。
fn func2<'a, T>(x: &'a (), y: T) -> impl Sized {
(x, y)
}
static mutへの参照の禁止
Rust 2024では、static mutな変数への参照は既定で禁止されるようになりました。
従来のRustでは、ミュータブルな静的変数への参照が(unsafeブロック内でという制約付きですが)記述可能でした。ただし、static_mut_refsリントが既定でwarnとなっているので、警告が発生する原因となります。
Rust 2024ではこの用法が望ましくないということで、static_mut_refsリントが既定でdeny、すなわち禁止となりました。以下のリストのコードは、Rust 2024以前では警告が出るものの許容されていましたが、Rust 2024ではエラーとなります。
static mut COUNTER: i32 = 0; // ミュータブルな静的変数
fn main() {
countup(); // 0
countup(); // 1
countup(); // 2
}
fn countup() {
unsafe {
let c = &mut COUNTER; // ミュータブルな静的変数への参照
// Rust 2024: creating a mutable reference to mutable static is discouraged
println!("{}", c);
*c += 1;
}
}
[NOTE]リント
リント(lint)とは、Rustのコンパイラが内蔵するコード改善のためのツールです。コンパイル時に出力されるnoteやwarning、errorはリントによるものです。さまざまなリントがあり、それらは指定されるレベル(allow、expect、warnなど6種類)に応じて動作が変化します。また、それぞれのリントには既定のレベルが設定されています。
