if let Geçici Kapsamı
Özet
if let $pat = $expr { .. } else { .. }ifadesinde,$exprdeğerlendirilirken oluşan geçici değerler artıkelsedalına girdikten sonra değil, programaelsedalına girmeden önce düşer.
Ayrıntılar
2024 sürümü, bir if let ifadesindeki scrutinee1 içinde bulunan
geçici değerlerin düşme kapsamını değiştirir. Amaç, geçicinin gereğinden
uzun yaşamasından doğan beklenmedik davranışları azaltmaktır.
2024’ten önce geçiciler if let ifadesinin ötesine uzatılabiliyordu. Örneğin:
#![allow(unused)]
fn main() {
// 2024'ten önce
use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
if let Some(x) = *value.read().unwrap() {
println!("deger {x}");
} else {
let mut v = value.write().unwrap();
if v.is_none() {
*v = Some(true);
}
}
// <--- 2021'de okuma kilidi burada düşer
}
}
Bu örnekte value.read() çağrısıyla oluşturulan geçici okuma kilidi, if let
ifadesi bitene kadar, yani else bloğundan sonrasına kadar düşmez. else
bloğu çalışırsa, yazma kilidi almaya çalıştığında kilitlenmeye neden olur.
2024 sürümü, geçicilerin ömrünü then-bloğu tamamen değerlendirildiği ya da
program denetiminin else bloğuna geçtiği noktaya kadar kısaltır.
#![allow(unused)]
fn main() {
// 2024 ile birlikte
use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
if let Some(x) = *value.read().unwrap() {
println!("deger {x}");
}
// <--- 2024'te okuma kilidi burada düşer
else {
let mut v = value.write().unwrap();
if v.is_none() {
*v = Some(true);
}
}
}
}
Geçici kapsamların nasıl genişletildiğine dair daha fazla bilgi için geçici kapsam kurallarına bakın. Kuyruk ifadelerine yapılan benzer bir değişiklik için kuyruk ifadesi geçici kapsamı bölümüne göz atın.
Taşıma
if let ifadesini match ile yeniden yazmak her zaman güvenlidir. match
scrutinee’sinin geçicileri match ifadesinin sonrasına, genelde ifadenin
bittiği noktaya kadar uzatılır. Bu, if letin 2021’deki davranışıyla aynıdır.
if_let_rescope lint’i, bu değişiklik yüzünden bir ömür sorunu doğduğunda ya da
if let scrutinee’sinden özel ve sıradan olmayan bir Drop yıkıcısına sahip
geçici değer üretildiğini tespit ettiğinde bir düzeltme önerir. Örneğin yukarıdaki
örnek, cargo fix önerisi kabul edildiğinde şöyle yeniden yazılabilir:
#![allow(unused)]
fn main() {
use std::sync::RwLock;
fn f(value: &RwLock<Option<bool>>) {
match *value.read().unwrap() {
Some(x) => {
println!("deger {x}");
}
_ => {
let mut s = value.write().unwrap();
if s.is_none() {
*s = Some(true);
}
}
}
// <--- Okuma kilidi hem 2021'de hem 2024'te burada düşer
}
}
Bu özel örnekte, az önce bahsedilen kilitlenme yüzünden bu muhtemelen istediğiniz
şey değildir. Ancak bazı senaryolar, geçicilerin else dalının ötesinde tutulduğunu
varsayıyor olabilir; böyle bir durumda eski davranışı korumak isteyebilirsiniz.
if_let_rescope lint’i, otomatik sürüm taşımasına dahil olan
rust-2024-compatibility lint grubunun parçasıdır. Kodunuzu Rust 2024 ile
uyumlu hale getirmek için şunu çalıştırın:
cargo fix --edition
Taşıma sonrasında if letten matche çevrilen tüm değişiklikleri gözden
geçirmeniz ve geçicilerin ne zaman düşmesi gerektiği açısından hangi davranışa
ihtiyaç duyduğunuza karar vermeniz önerilir. Eğer değişikliğin gereksiz olduğuna
karar verirseniz dönüşümü yeniden if lete çevirebilirsiniz.
Sürüm taşımasını yapmadan bu uyarıları elle incelemek isterseniz lint’i şu şekilde etkinleştirebilirsiniz:
#![allow(unused)]
fn main() {
// Elle taşıma yapmak için bunu crate köküne ekleyin.
#![warn(if_let_rescope)]
}