use Anahtar Kelimesi ile Yolları Kapsama Dahil Etme
Fonksiyonları çağırmak için yolları her seferinde uzun uzun yazmak zahmetli ve tekrarlayıcı gelebilir. Liste 7-7’de bekleme_listesine_ekle fonksiyonuna ulaşmak için ister mutlak ister göreli yolu seçelim, bekleme_listesine_ekle fonksiyonunu her çağırmak istediğimizde restoran_on_kisim ve karsilama modüllerini de belirtmek zorundaydık. Neyse ki bu süreci basitleştirmenin bir yolu vardır: use anahtar kelimesiyle bir yol için tek seferlik bir kısayol oluşturabilir ve ardından aynı kapsamın her yerinde bu daha kısa adı kullanabiliriz.
Liste 7-11’de, restoranda_yemek_ye içinde bekleme_listesine_ekle fonksiyonunu çağırmak için yalnızca karsilama::bekleme_listesine_ekle yazmamız yeterli olacak şekilde crate::restoran_on_kisim::karsilama modülünü restoranda_yemek_ye fonksiyonunun bulunduğu kapsama dahil ediyoruz.
mod restoran_on_kisim {
pub mod karsilama {
pub fn bekleme_listesine_ekle() {}
}
}
use crate::restoran_on_kisim::karsilama;
pub fn restoranda_yemek_ye() {
karsilama::bekleme_listesine_ekle();
}
use ile kapsama dahil etmeBir kapsama use ile bir yol eklemek, dosya sisteminde sembolik bağlantı (symbolic link) oluşturmaya benzer. Crate köküne use crate::restoran_on_kisim::karsilama eklediğimizde, tıpkı karsilama modülü doğrudan crate kökünde tanımlanmış gibi karsilama adı o kapsamda geçerli bir isim hâline gelir. use ile kapsama dahil edilen yollar da diğer tüm yollar gibi gizlilik kurallarına tabidir.
use deyiminin kısayolu yalnızca yazıldığı kapsam için oluşturduğuna dikkat edin. Liste 7-12, restoranda_yemek_ye fonksiyonunu musteri adlı yeni bir alt modüle taşır; bu alt modül use deyiminden farklı bir kapsam oluşturduğu için fonksiyon gövdesi derlenmeyecektir.
mod restoran_on_kisim {
pub mod karsilama {
pub fn bekleme_listesine_ekle() {}
}
}
use crate::restoran_on_kisim::karsilama;
mod musteri {
pub fn restoranda_yemek_ye() {
karsilama::bekleme_listesine_ekle();
}
}
use deyimi yalnızca bulunduğu kapsamda geçerlidir.Derleyici hatası, kısayolun musteri modülü içinde artık geçerli olmadığını gösterir:
$ cargo build
Compiling restoran v0.1.0 (file:///projects/restoran)
error[E0433]: cannot find module or crate `karsilama` in this scope
--> src/lib.rs:11:9
|
11 | karsilama::bekleme_listesine_ekle();
| ^^^^^^^^^ use of unresolved module or unlinked crate `karsilama`
|
= help: if you wanted to use a crate named `karsilama`, use `cargo add karsilama` to add it to your `Cargo.toml`
help: consider importing this module through its public re-export
|
10 + use crate::karsilama;
|
warning: unused import: `crate::restoran_on_kisim::karsilama`
--> src/lib.rs:7:5
|
7 | use crate::restoran_on_kisim::karsilama;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
For more information about this error, try `rustc --explain E0433`.
warning: `restoran` (lib) generated 1 warning
error: could not compile `restoran` (lib) due to 1 previous error; 1 warning emitted
Ayrıca use deyiminin kendi kapsamında artık kullanılmadığına dair bir uyarı (unused import) üretildiğine de dikkat edin! Bu sorunu çözmek için ya use deyimini de musteri modülünün içine taşıyabilir ya da musteri alt modülünün içinden üst modüldeki kısayola super::karsilama ile başvurabilirsiniz.
Not: Bir fonksiyonun içindeki { ... } bloklarının aksine mod musteri { ... } bildirimi yeni ve bağımsız bir modül ad alanı başlatır; bu nedenle üst modülde use ile tanımlanan kısayollar alt modüllerde doğrudan (önek olmadan) görünmez. Derleyici çıktısındaki cannot find module or crate `karsilama` in this scope (bu kapsamda karsilama modülü veya crate’i bulunamıyor) hatasının nedeni budur. Bununla birlikte alt modüller üst modüllerin gizli öğelerine erişebildiği için super::karsilama veya derleyicinin önerdiği use crate::karsilama; yoluyla üst modüldeki kısayolu kullanabilirsiniz. Aynı kapsam kuralı diğer yönde de işler: Bir use deyimi yalnızca dosyanın başına değil, bir fonksiyon gövdesinin içine de yazılabilir; bu durumda oluşturulan kısayol yalnızca o fonksiyon bloğunda geçerli olur.
Deyimsel use Yolları Oluşturma
Liste 7-11’de, Liste 7-13’teki gibi aynı sonucu elde etmek üzere use yolunu doğrudan bekleme_listesine_ekle fonksiyonuna kadar uzatmak yerine neden use crate::restoran_on_kisim::karsilama yazıp ardından restoranda_yemek_ye içinde karsilama::bekleme_listesine_ekle çağrısı yaptığımızı merak etmiş olabilirsiniz.
mod restoran_on_kisim {
pub mod karsilama {
pub fn bekleme_listesine_ekle() {}
}
}
use crate::restoran_on_kisim::karsilama::bekleme_listesine_ekle;
pub fn restoranda_yemek_ye() {
bekleme_listesine_ekle();
}
bekleme_listesine_ekle fonksiyonunu deyimsel olmayan bir biçimde use ile kapsama dahil etmeHem Liste 7-11 hem de Liste 7-13 aynı işi görse de bir fonksiyonu use ile kapsama dahil etmenin deyimsel (idiomatic) yolu Liste 7-11’deki kullanımdır. Fonksiyonun üst modülünü use ile kapsama dahil etmek, fonksiyonu çağırırken bu üst modülü belirtmemizi gerektirir. Çağrı noktasında üst modülü yazmak, tam yolun tekrarını en aza indirirken fonksiyonun yerel olarak tanımlanmadığını da açıkça gösterir. Liste 7-13’teki kodda ise bekleme_listesine_ekle fonksiyonunun nerede tanımlandığı belirsizdir.
Öte yandan struct’ları, enum’ları ve fonksiyon dışındaki diğer öğeleri use ile kapsama dahil ederken öğenin tam yolunu belirtmek deyimseldir. Liste 7-14, standart kütüphanedeki HashMap struct’ını bir ikili crate’in kapsamına dahil etmenin deyimsel yolunu gösterir.
use std::collections::HashMap;
fn main() {
let mut harita = HashMap::new();
harita.insert(1, 2);
}
HashMap struct’ını deyimsel biçimde kapsama dahil etmeBu kullanım geleneğinin arkasında teknik bir zorunluluk yoktur; yalnızca zaman içinde yerleşmiş bir uzlaşıdır ve geliştiriciler Rust kodunu bu biçimde okuyup yazmaya alışmıştır.
Bu geleneğin istisnası, use deyimleriyle aynı ada sahip iki öğeyi aynı kapsama dahil etmemiz gereken durumlardır; çünkü Rust aynı kapsamda isim çakışmasına izin vermez. Liste 7-15, aynı adı taşıyan ancak farklı üst modüllerde bulunan iki Result türünün kapsama nasıl dahil edileceğini ve bunlara nasıl başvurulacağını gösterir.
use std::fmt;
use std::io;
fn fonksiyon1() -> fmt::Result {
// --snip--
Ok(())
}
fn fonksiyon2() -> io::Result<()> {
// --snip--
Ok(())
}
Gördüğünüz gibi, üst modülleri kullanmak iki Result türünü birbirinden ayırt eder. Bunun yerine use std::fmt::Result ve use std::io::Result yazsaydık aynı kapsamda iki ayrı Result türümüz olurdu ve Result yazdığımızda Rust hangisini kastettiğimizi bilemezdi.
as Anahtar Kelimesiyle Yeni İsimler Verme
Aynı ada sahip iki türü use ile aynı kapsama dahil etme sorununun bir çözümü daha vardır: Yoldan sonra as anahtar kelimesini kullanarak türe yeni bir yerel ad, yani takma ad (alias) verebiliriz. Liste 7-16, iki Result türünden birini as ile yeniden adlandırarak Liste 7-15’teki kodu yazmanın alternatif yolunu gösterir.
use std::fmt::Result;
use std::io::Result as IoResult;
fn fonksiyon1() -> Result {
// --snip--
Ok(())
}
fn fonksiyon2() -> IoResult<()> {
// --snip--
Ok(())
}
as anahtar kelimesiyle yeniden adlandırmaİkinci use deyiminde std::io::Result türü için IoResult yeni adını seçtik; bu ad, aynı kapsama dahil ettiğimiz std::fmt içindeki Result ile çakışmayacaktır. Hem Liste 7-15 hem de Liste 7-16 deyimsel kabul edilir; dolayısıyla hangisini tercih edeceğiniz size kalmıştır!
pub use ile İsimleri Yeniden Dışa Aktarma
Bir ismi use anahtar kelimesiyle kapsama dahil ettiğimizde bu isim, içe aktarıldığı kapsam açısından gizli kalır. O kapsamın dışındaki kodların da bu isme sanki doğrudan o kapsamda tanımlanmış gibi başvurabilmesini sağlamak için pub ve use anahtar kelimelerini birleştirebiliriz. Bu tekniğe yeniden dışa aktarma (re-exporting) denir; çünkü bir öğeyi kendi kapsamımıza dahil ederken aynı zamanda başkalarının da kendi kapsamlarına dahil edebileceği şekilde dışarıya açmış oluruz.
Liste 7-17, Liste 7-11’deki kodda kök modülde yer alan use deyiminin pub use olarak değiştirilmiş hâlini gösterir.
mod restoran_on_kisim {
pub mod karsilama {
pub fn bekleme_listesine_ekle() {}
}
}
pub use crate::restoran_on_kisim::karsilama;
pub fn restoranda_yemek_ye() {
karsilama::bekleme_listesine_ekle();
}
pub use ile bir ismi yeni kapsam üzerinden dışarıdaki tüm kodların kullanımına açmaBu değişiklikten önce harici kodların bekleme_listesine_ekle fonksiyonunu restoran::restoran_on_kisim::karsilama::bekleme_listesine_ekle() yolunu kullanarak çağırması gerekirdi; üstelik bunun çalışabilmesi için restoran_on_kisim modülünün de pub olarak işaretlenmesi şarttı. Kök modüldeki pub use deyimi karsilama modülünü yeniden dışa aktardığından, harici kodlar artık doğrudan restoran::karsilama::bekleme_listesine_ekle() yolunu kullanabilir.
Yeniden dışa aktarma, kodunuzun iç yapısı ile kodunuzu çağıran geliştiricilerin problem alanını (domain) düşünme biçimi birbirinden farklı olduğunda son derece yararlıdır. Örneğin bu restoran benzetmesinde işletmeciler işleri “ön kısım” ve “arka kısım” ayrımıyla düşünür; oysa müşteriler restoranın bölümlerini büyük olasılıkla bu kavramlarla düşünmez. pub use sayesinde kodumuzu içeride bir yapıyla geliştirebilir, dışarıya ise daha yalın ve farklı bir yapıyla sunabiliriz. Böylece kütüphanemiz hem üzerinde çalışan geliştiriciler hem de onu kullanan programcılar için iyi düzenlenmiş olur. Bölüm 14’teki “Kullanışlı Bir Açık API Dışa Aktarmak” başlığında bir başka pub use örneğini ve bunun crate dokümantasyonunu nasıl etkilediğini inceleyeceğiz.
Not: Gerçek dünya Rust kütüphanelerinde yaygın bir mimari pratik, iç modülleri gizli (mod restoran_on_kisim; gibi pub olmadan) tutarak dosyaları mantıksal alt dizinlere ayırmak, dış kullanıcıların ihtiyaç duyacağı ana tür ve modülleri ise src/lib.rs içinde pub use ile düz ve ergonomik bir hiyerarşiyle dışa açmaktır. Bu yaklaşım geriye dönük uyumluluğu da kolaylaştırır: İleride iç modülleri yeniden adlandırdığınızda veya farklı dosyalara taşıdığınızda, src/lib.rs içindeki pub use satırlarını koruduğunuz sürece kütüphanenizi kullanan dış kodlar bu iç yeniden düzenlemeden hiç etkilenmez.
Harici Paketleri Kullanma
Bölüm 2’de rastgele sayılar üretmek için rand adlı harici bir paketi kullanan bir tahmin oyunu projesi geliştirdik. rand paketini projemizde kullanmak için Cargo.toml dosyamıza şu satırı eklemiştik:
rand = "0.10.1"
Cargo.toml dosyasına rand paketini bir bağımlılık olarak eklemek, Cargo’ya rand paketini ve onun ihtiyaç duyduğu bağımlılıkları crates.io adresinden indirip projemiz için kullanılabilir hâle getirmesini söyler.
Ardından rand tanımlarını paketimizin kapsamına dahil etmek için crate’in adı olan rand ile başlayan bir use satırı ekledik ve kapsama dahil etmek istediğimiz öğeleri belirttik. Bölüm 2’deki “Rastgele Bir Sayı Üretmek” kısmında rand::prelude modülündeki öğeleri kapsama dahil ettiğimizi ve rand::rng fonksiyonunu çağırdığımızı hatırlayın:
use std::io;
use rand::prelude::*;
fn main() {
println!("Sayıyı tahmin et!");
let gizli_sayi = rand::rng().random_range(1..=100);
println!("Gizli sayı: {gizli_sayi}");
println!("Lütfen tahmininizi girin.");
let mut tahmin = String::new();
io::stdin()
.read_line(&mut tahmin)
.expect("Satır okunamadı");
println!("Tahmininiz: {tahmin}");
}
Rust topluluğunun üyeleri crates.io adresinde pek çok paketi kullanıma sunmuştur ve bunlardan herhangi birini kendi paketinize dahil etmek aynı adımları gerektirir: Paketi Cargo.toml dosyanızda listelemek ve crate’indeki öğeleri kapsamınıza dahil etmek için use kullanmak.
Standart kütüphane olan std’nin de paketimiz açısından harici bir crate olduğuna dikkat edin. Standart kütüphane Rust diliyle birlikte geldiğinden std’yi dahil etmek için Cargo.toml dosyamızı değiştirmemize gerek yoktur. Ancak oradaki öğeleri paketimizin kapsamına dahil etmek için yine de use ile başvurmamız gerekir. Örneğin HashMap için şu satırı kullanırız:
#![allow(unused)]
fn main() {
use std::collections::HashMap;
}
Bu, standart kütüphane crate’inin adı olan std ile başlayan mutlak bir yoldur.
use Listelerini Sadeleştirmek İçin İç İçe Yollar Kullanma
Aynı crate’te ya da aynı modülde tanımlanmış birden çok öğeyi kullanıyorsak her bir öğeyi ayrı satırda listelemek dosyalarımızda dikey olarak çok yer kaplayabilir. Örneğin Liste 2-4’teki tahmin oyununda yer alan şu iki use deyimi std içinden öğeleri kapsama dahil eder:
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
use rand::prelude::*;
fn main() {
println!("Sayıyı tahmin et!");
let gizli_sayi = rand::rng().random_range(1..=100);
println!("Gizli sayı: {gizli_sayi}");
println!("Lütfen tahmininizi girin.");
let mut tahmin = String::new();
io::stdin()
.read_line(&mut tahmin)
.expect("Satır okunamadı");
println!("Tahmininiz: {tahmin}");
let tahmin: u32 = tahmin.trim().parse().expect("Lütfen bir sayı girin!");
match tahmin.cmp(&gizli_sayi) {
Ordering::Less => println!("Çok küçük!"),
Ordering::Greater => println!("Çok büyük!"),
Ordering::Equal => println!("Kazandınız!"),
}
}
Bunun yerine aynı öğeleri tek satırda kapsama dahil etmek için iç içe yollar (nested paths) kullanabiliriz. Bunu Liste 7-18’de gösterildiği gibi yolun ortak kısmını, ardından çift iki nokta üst üste işaretini (::) ve yolların farklılaşan kısımlarını süslü parantezler içinde listeleyerek yaparız.
use rand::prelude::*;
// --snip--
use std::{cmp::Ordering, io};
// --snip--
fn main() {
println!("Sayıyı tahmin et!");
let gizli_sayi = rand::rng().random_range(1..=100);
println!("Gizli sayı: {gizli_sayi}");
println!("Lütfen tahmininizi girin.");
let mut tahmin = String::new();
io::stdin()
.read_line(&mut tahmin)
.expect("Satır okunamadı");
let tahmin: u32 = tahmin.trim().parse().expect("Lütfen bir sayı yazın!");
println!("Tahmininiz: {tahmin}");
match tahmin.cmp(&gizli_sayi) {
Ordering::Less => println!("Çok küçük!"),
Ordering::Greater => println!("Çok büyük!"),
Ordering::Equal => println!("Kazandınız!"),
}
}
Daha büyük programlarda aynı crate’ten ya da modülden pek çok öğeyi iç içe yollarla kapsama dahil etmek, gereken ayrı use deyimlerinin sayısını önemli ölçüde azaltabilir!
İç içe yolu bir yolun herhangi bir seviyesinde kullanabiliriz; bu, alt yollardan birini paylaşan iki use deyimini birleştirirken işe yarar. Örneğin Liste 7-19 iki use deyimi gösterir: Biri std::io modülünü, diğeri ise std::io::Write öğesini kapsama dahil eder.
use std::io;
use std::io::Write;
use deyimiBu iki yolun ortak kısmı std::io’dur ve bu aynı zamanda birinci yolun tamamıdır. Bu iki yolu tek bir use deyiminde birleştirmek için Liste 7-20’de gösterildiği gibi iç içe yol içinde self anahtar kelimesini kullanabiliriz.
use std::io::{self, Write};
use deyiminde birleştirmeBu satır hem std::io modülünü hem de std::io::Write öğesini kapsama dahil eder.
Glob Operatörü ile Öğeleri İçe Aktarma
Bir yolda tanımlı tüm açık öğeleri kapsama dahil etmek istiyorsak o yolun ardından * glob operatörünü yazabiliriz:
#![allow(unused)]
fn main() {
use std::collections::*;
}
Bu use deyimi, std::collections içinde tanımlı tüm açık öğeleri geçerli kapsama dahil eder. Glob operatörünü kullanırken dikkatli olun! Glob kullanımı, hangi isimlerin kapsamda bulunduğunu ve programınızda geçen bir ismin nerede tanımlandığını anlamanızı zorlaştırabilir. Ayrıca kullandığınız bağımlılık kendi tanımlarını değiştirirse içe aktardığınız öğeler de değişir; örneğin bağımlılık, aynı kapsamda sizin tanımladığınız bir öğeyle aynı adı taşıyan yeni bir tanım eklerse bağımlılığı güncellediğinizde derleyici hatalarıyla karşılaşabilirsiniz.
Glob operatörü, test yazarken test edilecek her şeyi tests modülünün içine almak için sıkça kullanılır; bunu Bölüm 11’deki “Nasıl Test Yazılır” kısmında ele alacağız. Glob operatörü bazen prelude (ön tanımlı kapsam) deseninin bir parçası olarak da kullanılır: Bu desen hakkında daha fazla bilgi için standart kütüphane dokümantasyonuna bakabilirsiniz.
Not: Sıradan uygulama kodunda glob (*) operatöründen kaçınılmasının temel nedeni ad alanı kirliliği ve sürüm yükseltmelerinde yaşanabilecek sürpriz isim çakışmalarıdır; buna karşılık prelude ve tests modüllerinde durum farklıdır:
preludedeseni: Bir kütüphaneninpreludemodülü (örneğin yukarıda kullandığımızrand::prelude), kütüphanedeki öğelerin tamamını değil, yazarların glob ile güvenle içe aktarılmak üzere özenle seçip yeniden dışa aktardığı (pub use) az sayıdaki temel trait ve türü barındırır. Özellikle metot çağrılarının çalışabilmesi için kapsama alınması gereken trait’leri (RngExtgibi) kullanıcıya tek satırda (use rand::prelude::*;) sunmak amacıyla tasarlanmıştır.testsmodülü: Birim testleri genellikle test ettikleri kodla aynı dosyanın içindekimod testsalt modülünde yazılır veuse super::*;ile üst modüldeki tüm öğeleri (gizli olanlar dahil) tek seferde kapsama alır. Üst modül zaten sizin yazdığınız ve kontrol ettiğiniz kod olduğundan burada harici bağımlılık kaynaklı beklenmedik çakışma riski yoktur.