Kuyruk İfadesi Geçici Kapsamı
Özet
- Bir [fonksiyon] ya da kapanış gövdesinin veya bir [blok] ifadesinin kuyruk ifadesi değerlendirilirken oluşan geçici değerler artık yerel değişkenlerden önce düşebilir ve bazen bir sonraki daha büyük geçici kapsama uzatılmaz.
Ayrıntılar
2024 sürümü, kuyruk ifadelerdeki geçici değerlerin düşme sırasını değiştirir. 2024’ten önce kuyruk ifadedeki geçicilerin bloktan daha uzun yaşaması ve yerel değişken bağlarından daha sonra düşmesi çoğu zaman şaşırtıcı geliyordu:
#![allow(unused)]
fn main() {
// 2024'ten önce
use std::cell::RefCell;
fn f() -> usize {
let c = RefCell::new("..");
c.borrow().len() // error[E0597]: `c` does not live long enough
}
}
Bu kod 2021 sürümünde şu hatayı üretir:
error[E0597]: `c` does not live long enough
--> src/lib.rs:4:5
|
3 | let c = RefCell::new("..");
| - binding `c` declared here
4 | c.borrow().len() // error[E0597]: `c` does not live long enough
| ^---------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
5 | }
| -
| |
| `c` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
4 | let x = c.borrow().len(); x // error[E0597]: `c` does not live long enough
| +++++++ +++
For more information about this error, try `rustc --explain E0597`.
2021’de yerel c değişkeni, c.borrow() ile oluşturulan geçiciden önce düşer.
2024 sürümü bunu tersine çevirir; önce c.borrow() geçicisi düşer, ardından
yerel c değişkeni düşer ve böylece kod beklendiği gibi derlenir.
Geçici kapsam daralabilir
Bir ifadeyi değerlendirmek için geçici bir değer oluşturulduğunda, bu geçici geçici kapsam kurallarına göre düşer. Bu kurallar geçicinin ne kadar süre yaşatılacağını belirler. 2024’ten önce blokların kuyruk ifadelerinden gelen geçiciler, bir sonraki geçici kapsam sınırına kadar blok dışına uzatılırdı. Çoğu zaman bu, ifadenin ya da fonksiyon gövdesinin sonu olurdu. 2024’te ise kuyruk ifadenin geçicileri artık doğrudan blok sonunda, yani blok içindeki yerel değişkenlerden önce düşebilir.
Geçici kapsamın bu şekilde daralması, bazı programların 2024’te derlenememesine yol açabilir. Örneğin:
// Bu örnek 2021'de çalışır, ama 2024'te derlenmez.
fn main() {
let x = { &String::from("1234") }.len();
}
Bu örnekte 2021’de geçici String, blok dışına ve len() çağrısının ötesine
uzatılır, ardından ifadenin sonunda düşer. 2024’te ise blok biter bitmez düşer
ve ödünç alınmışken geçicinin düşmesiyle ilgili derleme hatası oluşur.
Bu tür durumlarda çözüm, blok ifadesini yerel bir değişkene taşımaktır; böylece geçici değer yeterince uzun yaşar:
fn main() {
let s = { &String::from("1234") };
let x = s.len();
}
Bu özel örnek, geçici ömür uzatımından yararlanır. Geçici ömür uzatımı,
geçicilerin normalden daha uzun yaşamasına izin veren belirli kurallar kümesidir.
Burada geçici String bir referans arkasında olduğu için bir sonraki ifadenin
üzerinde len() çağrısı yapabileceği kadar uzun süre uzatılır.
if let ifadelerinin geçici kapsamlarına yapılan benzer değişiklik için
if let geçici kapsamı bölümüne bakın.
Taşıma
Ne yazık ki kuyruk ifadedeki geçici değerlerin ömrünü kısaltırken anlamı birebir
koruyan yeniden yazımlar yoktur1. tail_expr_drop_order lint’i,
kuyruk ifadede özel ve sıradan olmayan bir Drop yıkıcısına sahip geçici değer
oluşturulup oluşturulmadığını tespit eder. cargo fix --edition çalıştırılırken
bu lint’ten uyarılar gelir; ancak otomatik değişiklik yapılmaz. Bu yüzden
uyarıları elle inceleyip ayarlama gerekip gerekmediğine karar vermeniz önerilir.
Sürüm taşımasını yapmadan bu uyarıları elle incelemek isterseniz lint’i şu şekilde açabilirsiniz:
#![allow(unused)]
fn main() {
// Elle taşıma yapmak için bunu crate köküne ekleyin.
#![warn(tail_expr_drop_order)]
}