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

Prelude’e Eklenenler

Özet

  • TryInto, TryFrom ve FromIterator trait’leri artık prelude’un bir parçasıdır.
  • Bu durum, trait metod çağrılarını belirsiz hale getirerek bazı kodların derlenememesine yol açabilir.

Ayrıntılar

Standart kütüphanenin prelude’u, her modülde otomatik olarak içe aktarılan her şeyi barındıran modüldür. Option, Vec, drop ve Clone gibi sık kullanılan öğeleri içerir.

Rust derleyicisi, prelude’a yapılan eklemelerin mevcut kodu bozmamasını sağlamak için elle içe aktarılan öğelere prelude’dan gelenlerden daha yüksek öncelik verir. Örneğin ornek adlı bir crate ya da modülünüz varsa ve içinde pub struct Option; tanımlanmışsa, use ornek::*; ifadesi Option adını tartışmasız biçimde ornek içindeki tipe bağlar; standart kütüphanedekine değil.

Ancak prelude’a bir trait eklemek, mevcut kodu daha ince bir şekilde bozabilir. Örneğin BenimTryInto trait’inden gelen x.try_into() çağrısı, std içindeki TryInto da içe aktarıldığında derlenmeyebilir. Çünkü bu kez try_into çağrısının hangi trait’ten geldiği belirsizleşir. TryIntoyu şimdiye kadar prelude’a eklememiş olmamızın nedeni de buydu; aksi halde çok sayıda kod kırılırdı.

Çözüm olarak Rust 2021 yeni bir prelude kullanır. Bu yeni sürüm, üç ek dışında mevcut prelude ile aynıdır:

Takip konusu burada bulunabilir.

Taşıma

2021 sürümünün bir parçası olarak, Rust 2018 kod tabanlarının Rust 2021’e otomatik taşınmasına yardımcı olmak için rust_2021_prelude_collisions taşıma lint’i eklendi.

Kodunuzu Rust 2021 ile uyumlu hale getirmek için şunu çalıştırın:

cargo fix --edition

Bu lint, yeni prelude trait’lerinden birindeki metodlarla aynı ada sahip fonksiyon ya da metod çağrılarını tespit eder. Bazı durumlarda, önceden çağırdığınız aynı işlevin çağrılmaya devam etmesini sağlamak için çağrıları yeniden yazabilir.

Kodunuzu elle taşımak ya da cargo fixin ne yaptığını daha iyi anlamak istiyorsanız, aşağıda taşımanın gerekli olduğu ve olmadığı durumları özetledik.

Taşıma gerekli

Çakışan trait metodları

Kapsamda bulunan iki trait aynı metod adına sahipse, hangi metodun kullanılacağı belirsiz olur. Örneğin:

trait BenimTrait<A> {
  // Bu ad, `std` içindeki `FromIterator` trait'inin `from_iter` metoduyla aynıdır.
  fn from_iter(x: Option<A>);
}

impl<T> BenimTrait<()> for Vec<T> {
  fn from_iter(_: Option<()>) {}
}

fn main() {
  // Vec<T>, hem `std::iter::FromIterator` hem de `BenimTrait` uygular
  // Eğer iki trait de kapsamdaysa (Rust 2021'de olduğu gibi),
  // hangi `from_iter` metodunun çağrılacağı belirsizleşir
  <Vec<i32>>::from_iter(None);
}

Bunu tam nitelikli sözdizimiyle düzeltebiliriz:

fn main() {
  // Artık hangi trait metoduna başvurduğumuz açık
  <Vec<i32> as BenimTrait<()>>::from_iter(None);
}

dyn Trait nesneleri üzerindeki doğal metodlar

Bazı kullanıcılar, metod adı yeni bir prelude trait’i ile çakışan bir dyn Trait değeri üzerinde metod çağırır:

#![allow(unused)]
fn main() {
mod alt_modul {
  pub trait BenimTrait {
    // Bu, `TryInto::try_into` ile aynı ada sahiptir
    fn try_into(&self) -> Result<u32, ()>;
  }
}

// `BenimTrait` burada kapsamda değildir ve yalnızca `alt_modul::BenimTrait` yoluyla anılabilir
fn islem(f: Box<dyn alt_modul::BenimTrait>) {
  // Eğer `std::convert::TryInto` kapsamdaysa (Rust 2021'de olduğu gibi),
  // hangi `try_into` metodunun çağrılacağı belirsizleşir
  f.try_into();
}
}

Statik gönderim metodlarının aksine, bir trait nesnesi üzerinde trait metod çağırmak için trait’in kapsamda olması gerekmez. Yukarıdaki kod, çakışan adlı başka bir trait kapsamda olmadığı sürece çalışır. TryInto trait’i kapsamda olduğunda ise belirsizlik doğar: Çağrı BenimTrait::try_into mu olmalı, yoksa std::convert::TryInto::try_into mu?

Bu gibi durumlarda ek dereference ekleyerek veya metod alıcısının türünü daha açık hale getirerek sorunu çözebiliriz. Böylece prelude trait’indeki metodlar yerine dyn Trait üzerindeki metod seçilir. Örneğin yukarıdaki f.try_into() çağrısını (&*f).try_into() biçimine çevirmek, çağrının yalnızca BenimTrait::try_into metoduna gideceğini garantiler.

Taşıma gerekmiyor

Doğal metodlar

Birçok tür, trait metodlarıyla aynı ada sahip kendi doğal metodlarını tanımlar. Örneğin aşağıda BenimYapi yapısı, standart kütüphanedeki FromIterator trait’inin metoduyla aynı ada sahip from_iter metodunu uygular:

#![allow(unused)]
fn main() {
use std::iter::IntoIterator;

struct BenimYapi {
  data: Vec<u32>
}

impl BenimYapi {
  // Bu, `std::iter::FromIterator::from_iter` ile aynı ada sahiptir
  fn from_iter(iter: impl IntoIterator<Item = u32>) -> Self {
    Self {
      data: iter.into_iter().collect()
    }
  }
}

impl std::iter::FromIterator<u32> for BenimYapi {
    fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
      Self {
        data: iter.into_iter().collect()
      }
    }
}
}

Doğal metodlar her zaman trait metodlarından önce gelir; bu yüzden burada bir taşıma gerekmez.

Uygulama Notu

Lint, 2021 sürümünü bir kod tabanına getirmenin ad çözümleme çakışmasına yol açıp açmayacağını belirlerken birkaç etkeni hesaba katmak zorundadır. Bu etkenler şunlardır:

  • Çağrı bir tam nitelikli çağrı mı, yoksa nokta ile metod çağrısı sözdizimi mi kullanıyor? Nokta ile metod çağrısı sözdiziminde otomatik referans ve otomatik dereference devreye girdiği için bu durum ad çözümlemesini etkiler. Elle referans alma ya da dereference yapma, nokta sözdiziminde önceliği açıkça belirlemeye yardım eder. Tam nitelikli çağrıda ise metod yolunda tür ve trait adı açıkça yazılmalıdır; örneğin <Tur as Trait>::metod.
  • Bu bir doğal metod mu, yoksa trait metodu mu? self alan doğal metodlar, trait metodlarından önce geldiği için TryInto::try_into çağrısına göre öncelik kazanır. Ancak &self ya da &mut self alan doğal metodlar, otomatik referans gerektirdikleri için aynı önceliği kazanmaz; TryInto::try_into ise self aldığı için buna ihtiyaç duymaz.
  • Bu metodun kaynağı core ya da std mi? Trait’ler kendi kendileriyle çakışamayacağı için bu önemlidir.
  • Verilen tür, çakışma yaşayabileceği trait’i gerçekten uyguluyor mu?
  • Metod dinamik gönderim üzerinden mi çağrılıyor? Yani self türü dyn Trait mi? Böyleyse, trait içe aktarmaları çözümlemeyi etkilemez ve taşıma lint’inin devreye girmesi gerekmez.