Rust'ın modül sistemi dört kavram üzerine kuruludur: paket, crate, modül ve yol. Paket Cargo.toml dosyasıyla tanımlanan ve bir ya da daha fazla crate içeren birimdir. Crate, derleyicinin tek seferde ele aldığı en küçük kod birimidir. Modüller crate içindeki kodu mod anahtar kelimesiyle gruplar ve gizliliği kontrol eder. Yollar ise crate::mutfak::pisir gibi bir öğeye nereden ulaşılacağını söyler. Kural basit: her şey varsayılan olarak gizlidir, dışarıya açmak istediğinizi pub ile işaretlersiniz; uzun yolları da use ile kısaltırsınız.
Bu yazıda bir kütüphane ve bir ikili crate içeren küçük bir Cargo paketi kurup modülleri ayrı dosyalara bölüyoruz, sonra gizlilik kurallarını, pub(crate) ve pub(super) gibi kısıtlı görünürlükleri, use yazımının inceliklerini, pub use ile yeniden dışa aktarmayı ve en sık karşılaşılan derleme hatalarını tek tek inceliyoruz. Paketin Cargo.toml tarafı, bağımlılıklar ve workspace yapısı Cargo.toml yazısında ayrıca ele alınıyor; Cargo'yu henüz kurmadıysanız kurulum yazısıyla başlayın.
Paket, crate ve crate kökü

Kitabın tanımıyla crate ya ikili crate ya da kütüphane crate olur. İkili crate çalıştırılabilir bir programa derlenir ve bir main fonksiyonu içermek zorundadır. Kütüphane crate main içermez, başka projelerin kullanacağı işlevselliği tanımlar. Derleyicinin işe başladığı kaynak dosyaya crate kökü denir ve bu dosyanın içeriği modül ağacının en tepesindeki örtük crate modülünü oluşturur. Bir paket istediği kadar ikili crate içerebilir ama en fazla bir kütüphane crate barındırabilir.
Cargo, crate köklerini Cargo.toml dosyasına yazmanıza gerek kalmadan geleneğe göre bulur: src/main.rs paket adıyla bir ikili crate'in, src/lib.rs paket adıyla bir kütüphane crate'in köküdür. İkisi birlikte varsa paketin iki crate'i olur. src/bin klasörüne koyduğunuz her dosya ayrı bir ikili crate sayılır. Kitabın önerdiği düzen, modül ağacını src/lib.rs içinde kurmak ve src/main.rs dosyasını yalnızca kütüphaneyi çağıran ince bir giriş noktası olarak tutmaktır. Böylece ikili crate, kütüphanenin herkese açık API'sini dışarıdan bir kullanıcı gibi kullanır ve siz de API'nizi tasarlarken onun ilk müşterisi olursunuz.
cargo new --vcs none restoran
# dizin yapısı (dosyaları elle ekledik):
# restoran/
# ├── Cargo.toml
# └── src/
# ├── main.rs ikili crate kökü
# ├── lib.rs kütüphane crate kökü
# ├── salon.rs mod salon
# ├── mutfak.rs mod mutfak
# └── mutfak/
# └── siparis.rs mod mutfak::siparisModülleri dosyalara bölmek
Crate kökünde mod mutfak; yazdığınızda derleyici modülün içeriğini üç yerde arar: süslü parantezlerle hemen orada yazılmış satır içi gövde, src/mutfak.rs dosyası ve eski stil olan src/mutfak/mod.rs dosyası. Alt modüller için arama, üst modülün adını taşıyan klasörde yapılır: src/mutfak.rs içindeki pub mod siparis; satırı src/mutfak/siparis.rs dosyasını yükler. Klasör yapısı modül ağacını birebir yansıtır.
// src/lib.rs
mod mutfak;
pub mod salon;
pub use crate::mutfak::siparis::Siparis;
fn sira_no(masa: u8) -> u32 {
1000 + masa as u32
}
pub fn servis_et(masa: u8, yemek: &str) -> String {
let s = mutfak::siparis::Siparis::yeni(masa, yemek);
mutfak::pisir(&s);
salon::masaya_gotur(&s)
}// src/mutfak.rs
pub mod siparis;
pub(crate) fn pisir(s: &siparis::Siparis) {
println!("[mutfak] {} hazırlanıyor (#{})", s.yemek, s.numara());
}
// src/mutfak/siparis.rs
#[derive(Debug)]
pub struct Siparis {
pub masa: u8,
pub yemek: String,
numara: u32,
}
impl Siparis {
pub fn yeni(masa: u8, yemek: &str) -> Siparis {
Siparis { masa, yemek: String::from(yemek), numara: crate::sira_no(masa) }
}
pub fn numara(&self) -> u32 {
self.numara
}
}
// src/salon.rs
use crate::Siparis;
pub fn masaya_gotur(s: &Siparis) -> String {
format!("{} numaralı masaya {} götürüldü", s.masa, s.yemek)
}// src/main.rs
use restoran::{salon, servis_et, Siparis};
fn main() {
println!("{}", servis_et(4, "mercimek çorbası"));
let s = Siparis::yeni(7, "pilav");
println!("{} / numara {}", salon::masaya_gotur(&s), s.numara());
}
// cargo run çıktısı:
// çıktı: [mutfak] mercimek çorbası hazırlanıyor (#1004)
// çıktı: 4 numaralı masaya mercimek çorbası götürüldü
// çıktı: 7 numaralı masaya pilav götürüldü / numara 1007Kitabın vurguladığı önemli nokta şu: mod bir dosya ekleme komutu değildir. Diğer dillerdeki include mantığıyla çalışmaz. Her dosya modül ağacına yalnızca bir kez, bir mod bildirimiyle bağlanır; projenin geri kalanı o koda bildirimin yapıldığı yerdeki yol üzerinden ulaşır. salon.rs dosyası Siparis tipini yeniden yüklemiyor, use crate::Siparis; ile mevcut yolu kapsamına alıyor. Aynı modül için hem mutfak.rs hem mutfak/mod.rs dosyası bulunursa derleyici hata verir. Farklı modüllerde iki stili karıştırmak serbesttir ama editörde çok sayıda mod.rs sekmesi açık kalacağı için kitap yeni stili önerir.
Gizlilik kuralları: varsayılan olarak her şey gizli
Fonksiyonlar, metotlar, struct'lar, enum'lar, modüller ve sabitler üst modüllerine karşı varsayılan olarak gizlidir. Reference iki erişim kuralı tanımlar. Birincisi: öğe pub ise, öğenin bütün üst modüllerine erişebildiğiniz her yerden erişilebilir. İkincisi: öğe gizliyse, yalnızca tanımlandığı modül ve o modülün alt modülleri erişebilir. Yani alt modüller, üst modüllerindeki gizli öğeleri görür; tersi geçerli değildir. Yukarıdaki örnekte sira_no crate kökünde gizli bir fonksiyon, ama mutfak::siparis kökün bir alt modülü olduğu için onu crate::sira_no yoluyla çağırabildi. Dışarıdaki main.rs ise bu fonksiyonu göremez.
Kitap burada sık atlanan bir ayrıntıya dikkat çeker: bir modülü pub yapmak içeriğini açmaz. pub mod raf yalnızca modülün adına erişim verir, içindeki fonksiyonlar ayrı ayrı pub işaretlenmedikçe gizli kalır. Aşağıdaki kodda modül açık ama fonksiyon değil, bu yüzden derleyici fonksiyonun gizli olduğunu söylüyor.
mod depo {
pub mod raf {
fn yerlestir() {}
}
}
fn main() {
depo::raf::yerlestir();
}
// error[E0603]: function `yerlestir` is privateStruct alanları ve enum varyantları
pub struct yazmak struct'ı açar ama alanları gizli bırakır; her alanı ayrı ayrı pub yapmanız gerekir. Gizli alanı olan bir struct'ı modül dışında süslü parantezli ifadeyle oluşturamazsınız, bu yüzden genellikle yeni gibi herkese açık bir kurucu fonksiyon sunarsınız. Bu, kullanıcının alanlara geçersiz değer koymasını engellemenin de en basit yoludur. Enum'larda durum tersidir: pub enum yazdığınızda bütün varyantlar açık olur. Kitap bunu kullanışlılıkla açıklar; varyantları gizli bir enum'ın pek bir işe yaramayacağını, struct'ların ise gizli alanlarla da anlamlı olduğunu söyler. Reference'a göre pub bir trait'in ilişkili öğeleri de varsayılan olarak açıktır.
mod hesap {
pub struct Kullanici {
pub ad: String,
sifre_ozeti: u64,
}
impl Kullanici {
pub fn yeni(ad: &str) -> Kullanici {
Kullanici { ad: String::from(ad), sifre_ozeti: 42 }
}
}
}
fn main() {
let k = hesap::Kullanici::yeni("zeynep");
println!("{}", k.sifre_ozeti);
}
// error[E0616]: field `sifre_ozeti` of struct `Kullanici` is privateYollar: crate, self, super
Bir öğeye mutlak ya da göreli yolla ulaşırsınız. Mutlak yol aynı crate için crate ile, harici bir crate için crate adıyla başlar; std::collections::HashMap da standart kütüphane crate'inden başlayan mutlak bir yoldur. Göreli yol bulunduğunuz modülden başlar ve self, super ya da aynı modüldeki bir isimle açılır. super dosya sistemindeki .. gibi bir üst modüle çıkar. Kitap genel olarak mutlak yolları tercih ettiğini söyler, çünkü öğe tanımlarını ve onları çağıran kodu birbirinden bağımsız taşımak daha olasıdır. Birlikte taşınacağını bildiğiniz yakın ilişkili modüllerde super kullanmak ise düzenlemeyi kolaylaştırır.
Görünürlüğü yalnızca ikiye ayırmak zorunda değilsiniz. Reference dört kısıtlı biçim tanımlar: pub(crate) öğeyi crate içinde görünür yapar, pub(super) yalnızca üst modüle açar, pub(self) hiç pub yazmamakla aynıdır, pub(in yol) ise belirttiğiniz üst modüle kadar açar. 2018 edition'dan itibaren pub(in ...) yolu crate, self ya da super ile başlamalıdır. Kütüphanenizin iç yardımcıları için pub(crate) çok kullanışlıdır: bütün crate kullanabilir ama dış kullanıcılar göremez. Restoran örneğindeki pisir fonksiyonu bu şekilde tanımlandı.
use std::collections::HashMap;
use std::fmt::Result as FmtResult;
use std::io::{self, Write};
mod magaza {
pub enum Durum {
Acik,
Kapali,
}
fn vergi(tutar: u32) -> u32 {
tutar / 5
}
pub mod kasa {
pub fn toplam(tutar: u32) -> u32 {
tutar + super::vergi(tutar)
}
pub(super) fn gun_sonu() -> &'static str {
"kasa kapandı"
}
}
pub fn kapat() -> &'static str {
self::kasa::gun_sonu()
}
pub(crate) const SUBE: &str = "Kadıköy";
}
fn bicim() -> FmtResult {
Ok(())
}
fn main() {
let mut stok: HashMap<&str, u32> = HashMap::new();
stok.insert("defter", 12);
let durum = magaza::Durum::Acik;
if let magaza::Durum::Acik = durum {
println!("{} şubesi açık", magaza::SUBE);
}
let _ = magaza::Durum::Kapali;
println!("{}", magaza::kasa::toplam(100));
println!("{}", magaza::kapat());
println!("{:?} {:?}", stok.get("defter"), bicim());
let mut cikis = io::stdout();
writeln!(cikis, "io::Write ile yazıldı").unwrap();
}
// çıktı: Kadıköy şubesi açık
// çıktı: 120
// çıktı: kasa kapandı
// çıktı: Some(12) Ok(())
// çıktı: io::Write ile yazıldıÖrnekte kasa::toplam, üst modüldeki gizli vergi fonksiyonuna super::vergi ile ulaşıyor; alt modül olduğu için buna hakkı var. gun_sonu ise pub(super) olduğu için yalnızca magaza modülünden çağrılabiliyor; kapat fonksiyonu onu self::kasa::gun_sonu() ile çağırıp dışarıya güvenli bir kapı açıyor. Aynı fonksiyonu main içinden çağırmayı denerseniz, derleyici onu yine gizli bir fonksiyon olarak raporlar.
mod magaza {
pub mod kasa {
pub(super) fn gun_sonu() {}
}
}
fn main() {
magaza::kasa::gun_sonu();
}
// error[E0603]: function `gun_sonu` is privateuse ile kısayol oluşturmak

use, bulunduğu kapsamda bir yola kısayol oluşturur; kitap bunu dosya sistemindeki sembolik bağlantıya benzetir. Kısayol yalnızca tanımlandığı kapsamda geçerlidir ve gizlilik denetimi normal yollarla aynıdır. Kök modülde yazılmış bir use alt modüllere kendiliğinden geçmez. Aşağıdaki kod bu yüzden derlenmez: yuvarla kısayolu kökte tanımlı, ama çağrı rapor modülünün içinde. Derleyici ayrıca kökteki use satırının kullanılmadığını da uyarır. Çözüm, use satırını alt modülün içine taşımak ya da super::yuvarla yazmaktır.
mod araclar {
pub fn yuvarla(x: f64) -> f64 {
(x * 100.0).round() / 100.0
}
}
use araclar::yuvarla;
mod rapor {
pub fn ozet(x: f64) -> f64 {
yuvarla(x)
}
}
fn main() {
println!("{}", rapor::ozet(3.14159));
}
// error[E0425]: cannot find function `yuvarla` in this scope
// Düzeltme: rapor modülünün içinde "use super::yuvarla;" yazın
// ya da çağrıyı super::yuvarla(x) yapın.- Fonksiyonlarda üst modülü getirin. Kitabın deyimsel önerisi
use crate::front_of_house::hosting;yazıphosting::add_to_waitlist()çağırmaktır. Böylece fonksiyonun yerel olmadığı çağrı noktasında görünür. - Tiplerde tam yolu getirin. Struct, enum ve diğer tipler için
use std::collections::HashMap;gibi tam yol yazmak gelenektir. - Aynı isimli iki tip için as kullanın.
std::fmt::Resultilestd::io::Resultaynı kapsama aynı adla giremez; ya üst modülleri getirirsiniz ya dause std::fmt::Result as FmtResult;ile birine takma ad verirsiniz. - İç içe yolları birleştirin.
use std::io::{self, Write};hemiomodülünü hemWritetrait'ini tek satırda getirir. - Glob operatörünü dikkatli kullanın.
use std::collections::*;bütün açık öğeleri getirir ama hangi ismin nereden geldiğini gizler; bağımlılık yeni bir isim eklediğinde çakışma çıkabilir. Kitap glob kullanımını en çok testlerde,use super::*;biçiminde görür.
pub use ile yeniden dışa aktarma
use ile getirilen isim, getirildiği kapsam için gizlidir. Başına pub eklerseniz o isim dışarıya da sunulur; buna yeniden dışa aktarma denir. Restoran örneğinde Siparis tipi aslında mutfak::siparis modülünde tanımlı ve mutfak modülü gizli. lib.rs içindeki pub use crate::mutfak::siparis::Siparis; satırı sayesinde dış kullanıcı tipe restoran::Siparis olarak erişiyor ve iç düzeni hiç bilmek zorunda kalmıyor. Kitap bunu, kodu yazanların düşündüğü iç yapı ile kullananların alan bilgisine uygun dış yapıyı birbirinden ayırmanın yolu olarak anlatır. Yeniden dışa aktarmanın belgelere etkisi için rustdoc yazısına bakabilirsiniz.
Sık karşılaşılan hatalar ve anlamları
| Hata kodu | Ne anlatıyor | Tipik çözüm |
|---|---|---|
| E0603 | Öğe ya da modül gizli | Öğeyi ve yol üzerindeki modülleri pub yapın veya pub(crate) gibi uygun görünürlüğü seçin |
| E0616 | Struct alanı gizli | Alanı pub yapın ya da erişim için bir metot sunun |
| E0425 | Bu kapsamda böyle bir isim yok | use satırını doğru kapsama taşıyın veya super:: ile tam yol yazın |
| E0433 | Yolun bir parçası çözülemedi | Modül adını, mod bildirimini ve crate adını kontrol edin |
Bunların dışında iki tuzak daha sık görülür. İlki dosyayı oluşturup mod bildirimini unutmaktır: derleyici yalnızca modül ağacına bağlanmış dosyaları derler, bildirimi olmayan bir .rs dosyası proje klasöründe dursa bile yok sayılır. İkincisi alt modül dosyasını yanlış klasöre koymaktır. hosting, front_of_house modülünün çocuğuysa dosyası src/front_of_house/hosting.rs olmalıdır; src/hosting.rs konumuna koyarsanız derleyici onu kökte bildirilmiş bir modül olarak arar. Bir crate içinde aynı öğeye birden çok yolla erişmek mümkündür ama bir öğe tek bir yerde tanımlanır; tanımın yerini değiştirmek yolları değiştirir, pub use ise eski yolu koruyarak geçişi kolaylaştırır.
Sık Sorulan Sorular
mod.rs mi kullanmalıyım, modul_adi.rs mi?
İkisi de destekleniyor. Kitap, çok sayıda aynı adlı dosya oluşmasını önlediği için src/mutfak.rs ve src/mutfak/siparis.rs düzenini önerir. Bir modül için iki stili aynı anda kullanmak hatadır.
use satırı derlemeyi etkiler mi?
Hayır. Hangi dosyaların derleneceğini mod bildirimleri belirler; use yalnızca isim kısayolu oluşturur. Kullanılmayan use satırları derleyici uyarısına yol açar.
Harici bir crate'i nasıl kullanırım?
Bağımlılığı Cargo.toml dosyasına ekledikten sonra yolu crate adıyla başlatırsınız. Standart kütüphane de harici bir crate'tir, ama Rust ile birlikte geldiği için Cargo.toml dosyasına eklemeniz gerekmez. Üçüncü taraf crate'lerin belgeleri için docs.rs adresine bakın.
Bir öğeyi yalnızca testlere açabilir miyim?
Test modülü alt modül olduğu için gizli öğelere zaten erişir. Entegrasyon testleri ise crate'i dışarıdan kullanır ve yalnızca pub API'yi görür; iç ayrıntıları test etmek için birim testleri tercih edin.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Rust öğrenme rehberi · Cargo.toml ve bağımlılıklar · Struct ve metotlar · Test yazma ve cargo test · rustdoc ve dokümantasyon · Rust nedir?