- 式
if let $pat = $expr { .. } else { .. }
において$expr
を評価する際に作られる一時値は、else
節以降が実行された後ではなく、実行される前にドロップされます。
2024 エディションでは、if let
式の被検査体 (scurtinee)1における一時値 (temporary value) のドロップスコープが変わります。
これは、一時値の生存期間が長すぎることによる、ときに想定外の挙動を避けるためのものです。
2024 より前は、以下のように、一時値が if let
式自体の末尾まで生存し続けていました。
// 2024 より前
# use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
if let Some(x) = *value.read().unwrap() {
println!("value is {x}");
} else {
let mut v = value.write().unwrap();
if v.is_none() {
*v = Some(true);
}
}
// <--- 読み取りロックはここでドロップされる
}
この例では、value.read()
によって生成された一時的な読み取りロックが、if let
式の末尾まで(else
節の末尾まで)ドロップされません。
else
節が実行された場合、書き込みロックを取得しようとしてデッドロックが発生してしまいます。
2024 エディションでは、一時値の生存期間は then 節(if let
の直後の { }
ブロック)の評価が完了したとき、あるいは else
節が開始するときに打ち切られます。
// 2024 以降
# use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
if let Some(x) = *value.read().unwrap() {
println!("value is {x}");
}
// <--- 2024 では、読み取りロックはここでドロップされる
else {
let mut s = value.write().unwrap();
if s.is_none() {
*s = Some(true);
}
}
}
一時スコープの範囲に関しての詳細は、一時値のスコープ規則をご参照ください。 末尾式の一時スコープの節では、末尾式に対する同様の変更について説明しています。
if let
はいつでも match
に安全に書き換えられます。
match
の被検査体で生成される一時値は、2021 における if let
の挙動と同様、少なくとも match
式の末尾まで(多くの場合、文の末尾まで)生存します。
if_let_rescope
リントは、本変更によってライフタイムの問題が発生する場合や、if let
の被検査体で生成される一時値が独自の非自明な Drop
デストラクタをもつ場合に、修正案を提示します。
例えば、前述の例に対して cargo fix
の提案を適用した場合、以下のように書き換えられます。
# use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
match *value.read().unwrap() {
Some(x) => {
println!("value is {x}");
}
_ => {
let mut s = value.write().unwrap();
if s.is_none() {
*s = Some(true);
}
}
}
// <--- Rust 2021 でも 2024 でも、読み取りロックはここでドロップされる
}
特に上記のコードでは、前述の通りデッドロックが起こるため望む挙動ではないでしょうが、場合によっては従来の挙動そのままに、一時値が else
節以降も生き延びることを意図することもあるでしょう。
if_let_rescope
リントは、自動エディション移行に含まれる rust-2024-compatibility
リントグループの一部です。
コードを Rust 2024 に移行するには、以下を実行します。
cargo fix --edition
移行後、if let
から match
へ変更された箇所をチェックし、一時値がドロップされるタイミングを再確認することを推奨します。
変更が不要と判断した場合は、if let
へ戻すとよいでしょう。
エディション移行ツールを使わずに手動で確認したい場合は、以下のリントをオンにしてください。
// クレートのトップレベルに以下を追加すると手動移行できる
#![warn(if_let_rescope)]
Footnotes
-
被検査体 (scurtinee) とは、
if let
式でマッチするかを検査される式(=
以降の式)のことです。 ↩