Rust'ta belgelendirme yorumları üç eğik çizgiyle (///) yazılır ve hemen ardından gelen öğeyi belgeler; //! ile başlayan yorumlar ise içinde bulundukları öğeyi, genellikle crate'in kendisini ya da bir modülü belgeler. Bu yorumlar Markdown destekler. cargo doc komutu Rust ile gelen rustdoc aracını çalıştırıp HTML belgeleri target/doc klasörüne üretir, cargo test ise belgelerdeki kod örneklerini test olarak derleyip çalıştırır. Böylece örnekleriniz kodla birlikte eskimez.
Bu yazıda rustdoc aracının temel kullanımını, iyi bir belge yorumunun yapısını, belge testlerini ve özniteliklerini, öğelere adla bağlantı vermeyi, rustdoc lint'lerini ve #[doc] özniteliğini çalışan bir kütüphane üzerinden ele alıyoruz. Normal birim ve entegrasyon testleri için cargo test yazısına, lint düzeylerinin genel mantığı için Clippy ve rustfmt yazısına bakabilirsiniz.
rustdoc ve cargo doc ne yapar?

rustdoc: What is rustdoc? sayfasına göre rustdoc argüman olarak bir crate kökü ya da Markdown dosyası alır ve HTML, CSS ile JavaScript üretir. Doğrudan rustdoc src/lib.rs çalıştırırsanız crate adını dosya adından çıkarmaya çalışır; doğru adı --crate-name ile vermeniz gerekir. Ayrıca varsayılan olarak yalnızca herkese açık öğeleri belgeler: pub olmayan bir fonksiyonun yorumu sayfada görünmez.
Cargo projelerinde bu ayrıntılarla uğraşmazsınız. cargo doc doğru --crate-name değerini, çıktı klasörünü ve bağımlılık yollarını kendisi verir; arka planda yaptığı çağrıyı cargo doc --verbose ile görebilirsiniz. cargo-doc(1) belgesine göre öne çıkan seçenekler şunlardır:
| Seçenek | Ne yapar? |
|---|---|
| --open | Belgeleri oluşturduktan sonra tarayıcıda açar |
| --no-deps | Bağımlılıkların belgelerini oluşturmaz |
| --document-private-items | Herkese açık olmayan öğeleri de ekler; ikili hedeflerde varsayılan olarak açıktır |
| -p, --package | Yalnızca belirtilen paketleri belgeler |
| --workspace | Çalışma alanındaki tüm üyeleri belgeler |
Belgeye göre belge üretimi birikimlidir: target/doc altındaki eski dosyalar sonraki çağrılarda korunur. Eski belgeleri temizlemek için cargo clean --doc kullanılır. Varsayılan olarak cargo doc paketin tüm kütüphane ve ikili hedeflerini belgeler; kütüphaneyle aynı adı taşıyan ikili hedef atlanır.
/// ve //! arasındaki fark
/// dış (outer) belgelendirmedir ve kendisinden sonra gelen öğeye, örneğin bir fonksiyona, yapıya ya da modüle aittir. //! iç (inner) belgelendirmedir ve içinde bulunduğu öğeyi belgeler. Crate kökünden önce hiçbir şey gelmediği için crate'in tamamını belgelemenin yolu src/lib.rs dosyasının başına //! satırları yazmaktır. Kitap, aynı yöntemin bir modül dosyasının başında modülü bir bütün olarak anlatmak için de kullanıldığını belirtir.
Normal yorumlar (//) belgeye girmez; kodun nasıl uygulandığını anlatmak için kalır. Kitabın ayrımıyla, belgelendirme yorumları crate'inizi kullanmak isteyen programcılar içindir, nasıl uygulandığını merak edenler için değil. Yorum türlerinin genel kullanımı fonksiyonlar ve yorumlar yazısında anlatılıyor.
İyi bir belge yorumunun yapısı
rustdoc: How to write documentation sayfası her öğe için şu iskeleti önerir: ne olduğunu anlatan kısa bir cümle, daha ayrıntılı bir açıklama, kullanıcının kopyalayıp deneyebileceği en az bir kod örneği ve gerekirse daha ileri açıklamalar. İlk boş satıra kadar olan metin arama sonuçlarında ve modül özetlerinde yeniden kullanılır; bu yüzden özeti tek satırda tutmak iyi bir alışkanlıktır. Belge ayrıca tip sisteminin fonksiyonun aldığı ve döndürdüğü tipleri zaten anlattığını, rustdoc imzadaki tüm tiplere bağlantı eklediği için bunları metinde tekrarlamanın fayda getirmediğini söyler.
Kitap ve rustdoc belgeleri sık kullanılan başlıkları da sayar. Bunların hepsi her öğede gerekmez, ama bir kontrol listesi olarak işe yarar:
- # Examples: öğenin nasıl kullanıldığını gösteren çalışan kod.
- # Panics: fonksiyonun hangi durumda panik oluşturduğu; çağıran bu durumlardan kaçınabilsin diye.
- # Errors:
Resultdöndüren fonksiyonlarda hangi hataların hangi koşulda geldiği. - # Safety:
unsafebir fonksiyonu çağıranın neyi garanti etmesi gerektiği. Ayrıntılar unsafe Rust yazısında.
Örnek kütüphane: belgelenmiş bir crate
Aşağıdaki src/lib.rs dosyası cargo new --lib belge ile oluşturulan bir projede denendi. Crate düzeyinde //! açıklaması, her herkese açık öğe için /// yorumları, farklı türde belge testleri ve öğeler arası bağlantılar içeriyor.
//! Basit fiyat hesaplama araçları.
//!
//! Bu crate, KDV eklemek ve indirim uygulamak için küçük
//! yardımcılar sunar. Ana tip [`Fiyat`]'tır.
#![warn(missing_docs)]
/// Kuruş cinsinden tutulan bir fiyat.
///
/// # Examples
///
/// ```
/// use belge::Fiyat;
///
/// let f = Fiyat::new(1000);
/// assert_eq!(f.kurus(), 1000);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Fiyat(u64);
impl Fiyat {
/// Kuruş değerinden yeni bir [`Fiyat`] oluşturur.
pub fn new(kurus: u64) -> Self {
Fiyat(kurus)
}
/// İçteki kuruş değerini döndürür.
pub fn kurus(&self) -> u64 {
self.0
}
/// Yüzde olarak verilen KDV'yi ekler; sonuç aşağı yuvarlanır.
///
/// # Examples
///
/// ```
/// # use belge::Fiyat;
/// let f = Fiyat::new(1000).kdv_ekle(20);
/// assert_eq!(f.kurus(), 1200);
/// ```
pub fn kdv_ekle(self, yuzde: u64) -> Self {
Fiyat(self.0 + self.0 * yuzde / 100)
}
/// İndirim uygular.
///
/// # Panics
///
/// `yuzde` 100'den büyükse panik oluşur.
///
/// ```should_panic
/// # use belge::Fiyat;
/// Fiyat::new(500).indirim(150);
/// ```
pub fn indirim(self, yuzde: u64) -> Self {
assert!(yuzde <= 100, "indirim yüzdesi 100'ü aşamaz");
Fiyat(self.0 - self.0 * yuzde / 100)
}
}
/// Tam lira yazılmış bir metinden fiyat ayrıştırır, örneğin `"12"`.
///
/// # Errors
///
/// Metin sayı değilse [`std::num::ParseIntError`] döner.
///
/// ```
/// # use belge::ayristir;
/// let f = ayristir(" 12 ")?;
/// assert_eq!(f.kurus(), 1200);
/// # Ok::<(), std::num::ParseIntError>(())
/// ```
///
/// Fiyat tipi değiştirilemez bir değerdir:
///
/// ```compile_fail
/// let f = belge::Fiyat::new(1);
/// f.0 = 5;
/// ```
pub fn ayristir(metin: &str) -> Result<Fiyat, std::num::ParseIntError> {
Ok(Fiyat(metin.trim().parse::<u64>()? * 100))
}Belge testleri cargo test ile diğer testlerle birlikte, ya da yalnız başına cargo test --doc ile çalışır. Çıktı Windows'ta alındığı için yollar ters bölüyle görünüyor:
$ cargo test --doc
Doc-tests belge
running 4 tests
test src\lib.rs - Fiyat (line 12) ... ok
test src\lib.rs - Fiyat::indirim (line 51) - should panic ... ok
test src\lib.rs - Fiyat::kdv_ekle (line 36) ... ok
test src\lib.rs - ayristir (line 67) ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
running 1 test
test src\lib.rs - ayristir (line 76) - compile fail ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
all doctests ran in 0.31s; merged doctests compilation took 0.24sBelge testleri nasıl çalışır?
![Belge testleri nasıl çalışır? unused_variables, dead_code gibi küçük örneklerde sık tetiklenen bazı allow..., #![doc(test(attr(...)))] ile verilen öznitelikler eklenir; baştaki...](/assets/blog/rust-rustdoc-dokumantasyon-yorumlari-b5.webp)
rustdoc: Documentation tests sayfasına göre belge testleri normal birim testleri gibi değerlendirilir: derlenip panik oluşturmadan çalışırsa geçer. Bir sonucu doğrulamak için assert_eq! gibi makroları kullanırsınız; değer değişirse test başarısız olur. Örnekte her kod bloğunun başına fn main yazmamıza gerek kalmadı, çünkü rustdoc örnekleri çalıştırmadan önce ön işlemden geçirir. Belgedeki algoritmanın özeti şudur:
unused_variables,dead_codegibi küçük örneklerde sık tetiklenen bazıallowöznitelikleri eklenir.#![doc(test(attr(...)))]ile verilen öznitelikler eklenir; baştaki#![...]öznitelikleri crate özniteliği olarak korunur.- Örnek
extern crateiçermiyorsa ve bu davranış kapatılmamışsa sizin crate'iniz içinextern crate <crate_adı>;eklenir. - Örnek
fn mainiçermiyorsa geri kalan kodfn main() { ... }içine sarılır.
# ve bir boşlukla başlayan satırlar derlenir ama belgede gösterilmez. Örnekteki # use belge::Fiyat; satırları bu sayede okuyucuyu yormaz. Bu teknik özellikle ? operatörü için gereklidir: ? bir Result döndürür, ama sarılmış main hiçbir şey döndürmediği için tip uyuşmazlığı oluşur. Belge iki çözüm verir: gizli satırlarla Result döndüren bir main yazmak ya da örneğin sonuna # Ok::<(), HataTipi>(()) eklemek. İkincisinde (()) kısmı arada boşluk olmadan yazılmalıdır, aksi halde rustdoc örneği Result döndüren bir fonksiyon olarak tanımaz. Hata tipini açıkça yazmak da gerekir, çünkü ? örtük bir dönüşüm eklediği için tip çıkarımı tek bir sonuca varamaz.
Belge ayrıca örneklerde unwrap() yerine bu tür gizli hata yönetimini tercih etmenizi önerir; okuyucu örneği kendi koduna kopyaladığında daha iyi bir alışkanlık edinir. Bir örneğin değeri değiştiğinde ne olduğunu görmek için kdv_ekle örneğindeki beklenen değeri bilerek 1300 yaptık. Test şu çıktıyla başarısız oldu:
---- src\lib.rs - Fiyat::kdv_ekle (line 36) stdout ----
Test executable failed (exit status: 101).
stderr:
thread 'main' (77464) panicked at C:\Users\...\doctest_bundle_2024.rs:28:1:
assertion `left == right` failed
left: 1200
right: 1300Geçici dosya yolunu kısalttık; iş parçacığı numarası her çalıştırmada değişir. İlginç bir ayrıntı daha: aynı dosyadaki ayristir örneğinde de 1200 değeri geçtiği için sed ile yaptığımız değişiklik iki testi birden bozdu ve ikisi de rapora girdi.
Kod bloğu öznitelikleri
Kod çitinin açılış satırına yazılan öznitelikler, rustdoc'a örneği nasıl ele alacağını söyler. Dil belirtilmemiş bir blok Rust kodu kabul edilir; Rust olmayan bir içerik için text yazmanız gerekir.
| Öznitelik | Davranış | Ne zaman? |
|---|---|---|
| (yok) veya rust | Derlenir ve çalıştırılır | Varsayılan; çoğu örnek için |
| should_panic | Derlenmeli ve panik oluşturmalı | # Panics bölümündeki örnekler |
| no_run | Derlenir ama çalıştırılmaz | Ağ erişimi gerektiren ya da sonsuz döngülü örnekler |
| compile_fail | Derleme başarısız olmalı | Tip sisteminin neyi engellediğini göstermek |
| ignore | Hiç derlenmez | Eksik ya da sözde kod; nadiren doğru seçim |
| edition2024 vb. | Belirtilen edition ile derlenir | Edition farkını göstermek |
| standalone_crate | Diğer testlerle birleştirilmez | Satır numarası gibi ortama bağlı örnekler |
| text | Rust kodu sayılmaz | Çıktı veya düz metin |
Belge ignore özniteliği için uyarır: en genel seçenektir ve neredeyse hiçbir zaman istediğiniz şey değildir; kod değilse text, kodsa # satırlarıyla çalışan bir örnek tercih edilmelidir. Kullanacaksanız nedenini parantez içinde yazmak gelenektir. compile_fail için de bir not düşer: bugünkü sürümde derlenmeyen kod, dile yeni özellikler eklendikçe ileride derlenebilir. Örneğimizdeki compile_fail testi, dış crate'ten özel alana erişilemediğini gösteriyor.
Rust 2024: birleştirilmiş belge testleri
Çıktının son satırındaki merged doctests compilation ifadesi bir 2024 edition değişikliğidir. Edition Guide'a göre önceki edition'larda rustdoc her kod bloğunu ayrı bir çalıştırılabilir dosya olarak derliyordu ve çok sayıda örnek olduğunda bu ciddi bir yük getiriyordu. 2024 edition'da uyumlu örnekler tek bir ikili dosyada birleştirilip derlenir; her örnek bu dosyada ayrı bir fonksiyon olur ve testler yine ayrı süreçlerde çalışır. Bizim çıktımızda da dört test bir grupta, compile_fail testi ise ayrı bir grupta koştu; son satır birleştirilmiş derlemenin süresini ayrıca raporluyor.
Bazı örnekler birleştirmeye uygun değildir. Belgedeki örnek, std::panic::Location::caller() ile satır numarasını doğrulayan bir testtir: başka bir örnek eklendiğinde satır kayar ve test bozulur. Bu durumda bloğa standalone_crate özniteliği eklenir. Rustdoc bazı uyumsuz durumları kendisi de algılar. Diğer edition değişiklikleri için 2024 edition yazısına bakabilirsiniz.
Öğelere adla bağlantı (intra-doc links)
rustdoc: Linking to items by name sayfasına göre köşeli parantez içine bir öğenin yolunu yazmak, o öğenin belge sayfasına bağlantı üretir. [`Fiyat`] yazdığınızda ters tırnaklar kaldırılır ve bağlantı Fiyat sayfasına gider. Kapsamdaki her şeye bağlantı verebilirsiniz; Self, self, super ve crate içeren yollar, ilişkili fonksiyonlar ve sabitler, standart kütüphanedeki ilkel tipler de desteklenir. Vec<T> gibi generic parametreli yazımlar Vec sayfasına çözülür. Örnekteki [`std::num::ParseIntError`] bağlantısı da standart kütüphane belgesine gider.
Bağlantı çözülemezse rustdoc varsayılan olarak uyarır. Örnek crate'e var olmayan bir tipe bağlantı veren bir yorum ekleyip cargo doc çalıştırdık:
/// Ayrıntılar için [`Kampanya`] tipine bakın.
pub fn belgesiz() {}
// cargo doc çıktısı:
// warning: unresolved link to `Kampanya`Uyarının notunda bunun rustdoc::broken_intra_doc_links lint'i olduğu ve varsayılan olarak açık olduğu yazıyor. Yardım satırı da köşeli parantezi bağlantı olarak değil düz metin olarak kullanmak istiyorsanız önüne ters bölü koymanızı öneriyor.
rustdoc lint'leri ve missing_docs

rustdoc: Lints sayfası rustdoc'a özgü lint'leri listeler. Bunlar rustdoc:: önekiyle yazılır ve missing_docs dışında yalnızca rustdoc çalışırken etkindir. Öne çıkanlar:
missing_docs: varsayılan olarak kapalıdır ve belgesiz herkese açık öğeleri bulur. Diğerlerinden farklı olarak doğrudanrustctarafından da sağlanır; örnekteki#![warn(missing_docs)]satırı belgesiz bir fonksiyon eklediğimizde hemcargo testsırasındaki derlemede hemcargo docçalışırkenwarning: missing documentation for a functionuyarısı üretti.rustdoc::broken_intra_doc_links: varsayılan olarak uyarır; çözülemeyen bağlantıları bulur.rustdoc::private_intra_doc_links: varsayılan olarak uyarır; herkese açık bir öğenin belgesinden özel bir öğeye verilen bağlantıları yakalar.rustdoc::missing_crate_level_docs: varsayılan olarak kapalıdır; crate kökünde belge yoksa uyarır. Belgeye göre ileride uyarı düzeyine çekilmesi amaçlanıyor.rustdoc::invalid_codeblock_attributes,rustdoc::bare_urls,rustdoc::invalid_html_tags: yanlış yazılmış kod bloğu öznitelikleri, bağlantıya çevrilmemiş çıplak adresler ve hatalı HTML etiketleri için.
Belge, kütüphane yazarları için #![deny(missing_docs)] özniteliğinin projenin belgesiz kalmasını önlemenin, #![warn(missing_docs)] özniteliğinin ise kapsamlı belgelendirmeye doğru ilerlemenin iyi bir yolu olduğunu söyler.
Markdown ve rustdoc uzantıları
Rustdoc CommonMark Markdown belirtimini kullanır ve buna birkaç uzantı ekler: tilde ile üstü çizili metin, dipnotlar, boru ve tirelerle tablolar, görev listeleri ve akıllı noktalama. Akıllı noktalama bazı ASCII dizilerini tipografik karakterlere çevirir; örneğin art arda yazılmış iki veya üç kısa çizgi uzun çizgilere dönüşür. Belgede bu dönüşümü istemiyorsanız ilgili metni kod içine alın.
Sık yapılan hatalar
- Özeti iki paragraf uzunluğunda yazmak; ilk boş satıra kadar olan kısım arama ve modül sayfalarında görünür.
?içeren örnekteOk::<(), HataTipi>(())satırını unutmak ya da(())içine boşluk koymak.- Çalışmayan her örneği
ignoreile işaretlemek; çoğu durumda#ile gizlenen kurulum satırları ya dano_rundaha doğrudur. - Özel bir öğeyi belgeleyip sayfada neden görünmediğini merak etmek; varsayılan çıktı yalnızca herkese açık öğeleri içerir.
- Belge testlerinin kütüphane hedefi için çalıştığını unutmak; Cargo belgesine göre
cargo testkod örneklerini kütüphane hedefinin belge yorumlarından çıkarır. Örneklerin sınanmasını istiyorsanız mantığılib.rsdosyasına taşıyın. - Satır numarası gibi ortama bağlı bilgileri test eden örnekleri
standalone_crateolmadan bırakmak.
Kütüphane ve ikili crate ayrımının testlere etkisi test yazısında ayrıntılı ele alınıyor; projenin lib.rs ve main.rs olarak bölünmesi için de modül sistemi yazısına bakabilirsiniz.
Sık Sorulan Sorular
Belgeyi nasıl görüntülerim?
cargo doc --open belgeleri oluşturup varsayılan tarayıcıda açar. Başka bir tarayıcı için BROWSER ortam değişkenini ya da doc.browser Cargo ayarını kullanabilirsiniz. Dosyalar target/doc/<crate_adı>/index.html altında durur.
Belge testleri ne zaman çalışır?
cargo test birim ve entegrasyon testlerinden sonra Doc-tests başlığı altında belge testlerini de çalıştırır. Yalnızca bunları çalıştırmak için cargo test --doc kullanın. Belge testlerinde derleyici uyarılarını görmek isterseniz belge, cargo test --doc -- --show-output komutunu önerir.
Bağımlılıkların belgelerini de görebilir miyim?
Evet. cargo doc varsayılan olarak yerel paketle birlikte tüm bağımlılıkları da belgeler. Yalnızca kendi crate'inizi istiyorsanız --no-deps verin. Bağımlılık tanımları için Cargo.toml yazısına bakabilirsiniz.
README dosyamı da test edebilir miyim?
Belgeye göre #[doc = include_str!("../README.md")] özniteliğini #[cfg(doctest)] ile işaretli, gizli bir yapıya uygulayarak README içindeki Rust örneklerini diğer belge testleriyle birlikte çalıştırabilirsiniz. Rustdoc cfg(doctest) koşulunu yalnızca belge testleri için derleme yaparken ayarlar; bu yapı herkese açık belgede görünmez.
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 · Rust nedir? · Test yazma ve cargo test · Clippy ve rustfmt · macro_rules! ile makro yazma · Cargo.toml ve bağımlılıklar