Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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)]
}

  1. Ayrıntılar RFC 3606 içinde belgelenmiştir.