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

Never Türü Geri Dönüş Değişikliği

Özet

  • Never türünden (!) herhangi bir türe (“never-to-any”) yapılan zorlama artık birim türüne (()) değil, yine never türüne (!) geri döner.
  • never_type_fallback_flowing_into_unsafe lint’i artık varsayılan olarak denydir.

Ayrıntılar

Derleyici, bir [zorlama noktasında][] ! (never) türünde bir değer gördüğünde, tür denetleyicisinin herhangi bir türü çıkarabilmesine izin vermek için örtük bir zorlama ekler:

#![allow(unused)]
fn main() {
#![feature(never_type)]
// Bu:
let x: u8 = panic!();

// ...derleyici tarafından özünde şuna dönüştürülür:
let x: u8 = absurd(panic!());

// ...buradaki `absurd` aşağıdaki fonksiyondur
// (`!` her zaman ulaşılamaz kodu işaretlediği için bu güvenlidir):
fn absurd<T>(x: !) -> T { x }
}

Tür çıkarılamıyorsa bu durum derleme hatalarına yol açabilir:

#![allow(unused)]
fn main() {
#![feature(never_type)]
fn absurd<T>(x: !) -> T { x }
// Bu:
{ panic!() };

// ...şuna dönüştürülür:
{ absurd(panic!()) }; //~ HATA `absurd` icin tur cikarilamiyor
}

Bu tür hataları önlemek için derleyici, absurd çağrılarını nereye eklediğini hatırlar ve türü çıkaramazsa bunun yerine geri dönüş türünü kullanır:

#![allow(unused)]
fn main() {
#![feature(never_type)]
fn absurd<T>(x: !) -> T { x }
type Fallback = /* Keyfi seçilmiş bir tür! */ !;
{ absurd::<Fallback>(panic!()) }
}

Buna “never türü geri dönüşü” denir.

Tarihsel olarak geri dönüş türü () idi. Bu, geri dönüş mekanizması olmasa derleyicinin () çıkaramayacağı durumlarda bile ! türünün kendiliğinden () türüne zorlanmasına neden oluyordu. Bu kafa karıştırıcıydı ve ! türünün kararlı hale gelmesini engelliyordu.

2024 sürümünde geri dönüş türü artık ! oldu. Bu değişikliği daha sonra tüm sürümlere yaymayı planlıyoruz. Böylece davranış daha sezgisel hale gelir. Artık ! verdiğinizde ve onu başka bir şeye zorlamak için neden yoksa, değer ! olarak kalır.

Bazı durumlarda kodunuz geri dönüş türünün () olmasına dayanıyor olabilir; bu yüzden bu değişiklik derleme hatalarına ya da davranış değişikliklerine yol açabilir.

never_type_fallback_flowing_into_unsafe

never_type_fallback_flowing_into_unsafe lint’inin varsayılan düzeyi 2024 sürümünde warndan denyye yükseltildi. Bu lint, ! geri dönüşü ile unsafe kod arasındaki ve tanımsız davranışa yol açabilecek belirli bir etkileşimi tespit etmeye yardım eder. Tam açıklama için bağlantıya bakın.

Taşıma

Otomatik bir düzeltme yoktur; ancak sürüm değişikliğiyle bozulacak kod otomatik olarak tespit edilir. Önceki sürümdeyken bile kodunuz bozulacaksa uyarı görürsünüz.

Çözüm, geri dönüş türü kullanılmasın diye türü açıkça belirtmektir. Ne yazık ki hangi türün yazılması gerektiğini görmek her zaman kolay olmayabilir.

Bu değişiklikle bozulan en yaygın kalıplardan biri, f fonksiyonu dönüş türünün Ok kısmı üzerinde jenerikken f()?; kullanımıdır:

#![allow(unused)]
fn main() {
#![allow(dependency_on_unit_never_type_fallback)]
fn outer<T>(x: T) -> Result<T, ()> {
fn f<T: Default>() -> Result<T, ()> {
    Ok(T::default())
}

f()?;
Ok(x)
}
}

Bu örnekte T türünün çıkarılamayacağını düşünebilirsiniz. Ancak ? işlecin şu anki sözdizimsel açılımı nedeniyle tür önceden () olarak çıkarılıyordu; artık ! olarak çıkarılacak.

Sorunu çözmek için T türünü açıkça belirtmelisiniz:

#![allow(unused)]
fn main() {
#![deny(dependency_on_unit_never_type_fallback)]
fn outer<T>(x: T) -> Result<T, ()> {
fn f<T: Default>() -> Result<T, ()> {
    Ok(T::default())
}
f::<()>()?;
// ...ya da:
() = f()?;
Ok(x)
}
}

Bir diğer görece yaygın durum, kapanış içinde panic üretmektir:

#![allow(unused)]
fn main() {
#![allow(dependency_on_unit_never_type_fallback)]
trait Unit {}
impl Unit for () {}

fn run<R: Unit>(f: impl FnOnce() -> R) {
    f();
}

run(|| panic!());
}

Daha önce panic! içinden gelen !, Unit uygulayan () türüne zorlanıyordu. Artık ! olduğu gibi kaldığı için bu kod, ! Unit uygulamadığından başarısız olur. Bunu çözmek için kapanışın dönüş türünü açıkça belirtebilirsiniz:

#![allow(unused)]
fn main() {
#![deny(dependency_on_unit_never_type_fallback)]
trait Unit {}
impl Unit for () {}

fn run<R: Unit>(f: impl FnOnce() -> R) {
    f();
}
run(|| -> () { panic!() });
}

f()? örneğine benzer bir durum, bir dalda ! türünde ifade ve diğer dalda dönüş türü kısıtlanmamış bir fonksiyon kullanıldığında da görülür:

#![allow(unused)]
fn main() {
#![allow(dependency_on_unit_never_type_fallback)]
if true {
    Default::default()
} else {
    return
};
}

Önceden return içinden gelen ! yanlış şekilde () türüne zorlandığı için Default::default() dönüş türü olarak () çıkarılıyordu. Artık bunun yerine ! çıkarılacak; bu yüzden ! Default uygulamadığından kod derlenmeyecektir.

Yine çözüm, türü açıkça belirtmektir:

#![allow(unused)]
fn main() {
#![deny(dependency_on_unit_never_type_fallback)]
() = if true {
    Default::default()
} else {
    return
};

// ...ya da:

if true {
    <() as Default>::default()
} else {
    return
};
}