Cargo.toml, Cargo'nun her paket için okuduğu manifest dosyasıdır ve TOML biçiminde yazılır. [package] bölümü paketin adını, sürümünü ve edition değerini; [dependencies] bölümü hangi crate'lere hangi sürüm aralığıyla bağlı olduğunuzu; [features] koşullu derleme seçeneklerini; [profile.*] derleyici ayarlarını; [workspace] ise birlikte yönetilen paket gruplarını tanımlar. Cargo.lock ise bu geniş tanımların çözülmüş halini, yani her bağımlılığın tam olarak hangi sürümle derlendiğini tutar; onu Cargo yazar, siz elle düzenlemezsiniz.

Bu yazıda manifestin temel alanlarını, bağımlılık belirtme biçimlerini ve sürüm gereksinimi söz dizimini, Cargo.lock dosyasının rolünü, features, profiller ve workspace kavramlarını The Cargo Book'a dayanarak anlatıyoruz. Örnekler ağ bağlantısı gerektirmeyecek şekilde, birbirine path ile bağlı iki yerel paketten oluşan bir workspace üzerinde Cargo 1.99.1 ile --offline çalıştırıldı. crates.io ve git bağımlılıklarının söz dizimini ise derlemeden, yalnızca TOML olarak gösteriyoruz. Cargo'yu ilk kez kuruyorsanız Rust kurulumu: rustup ve cargo yazısından başlayın; serinin tamamı Rust öğrenme rehberi sayfasında.

[package] bölümü: name, version, edition ve rust-version

Cargo'nun zorunlu tuttuğu tek alan name alanıdır. Paket adı yalnızca harf, rakam, - ve _ içerebilir; cargo new ek olarak adın geçerli bir Rust tanımlayıcısı olmasını ister, crates.io da yalnızca ASCII karakterler ve en fazla 64 karakter gibi ek kurallar uygular. version alanı SemVer biçimindedir (major.minor.patch, isteğe bağlı -alpha gibi bir ön sürüm eki); belirtilmezse 0.0.0 kabul edilir, ama yayımlamak için gereklidir. Manifest belgesindeki MSRV notuna göre bu alan 1.75 öncesinde zorunluydu.

edition alanı paketin hangi Rust edition'ı ile derleneceğini belirler ve test, örnek ve ikili dosyalar dahil paketin tüm hedeflerini etkiler. Belgeye göre cargo new şu an 2024 edition ile manifest oluşturur; alan hiç yoksa geriye uyumluluk için 2015 edition varsayılır. Bu yüzden elle oluşturduğunuz manifestlerde edition satırını unutmamanız önemlidir. rust-version alanı paketinizin desteklediği en düşük Rust sürümünü bildirir: daha eski bir araç zinciriyle derlenmeye çalışıldığında Cargo, anlaşılması zor bir söz dizimi hatası yerine doğrudan bu uyumsuzluğu bildirir. Edition değişiklikleri için Rust 2024 edition değişiklikleri yazısına bakabilirsiniz.

Diğer alanların çoğu yayımlama ve kayıt (registry) içindir: description, license, repository, readme, keywords, categories. publish = false paketin yanlışlıkla yayımlanmasını engeller, default-run birden çok ikili dosya varken cargo run komutunun hangisini çalıştıracağını seçer. Manifestte ayrıca [lints] bölümü de bulunur; lint seviyelerini Clippy ve rustfmt yazısında ele alıyoruz.

Bağımlılık belirtme biçimleri

Bağımlılık belirtme biçimleri: crates.io, yalnızca depo adresi verirseniz varsayılan dalın son commit'i kullanılır. branch, tag veya rev ile belirli..., path, Platforma özel

Aşağıdaki manifest, The Cargo Book'taki örneklerden derlenmiş bir özet. Bu dosyayı derlemedik, çünkü crates.io ve git kaynakları ağ erişimi gerektirir; amacı her biçimin söz dizimini tek yerde göstermektir.

toml
[package]
name = "ornek"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"

[dependencies]
# crates.io: "1.2.3" yazımı >=1.2.3, <2.0.0 aralığı demektir
time = "0.1.12"
# git deposu; branch, tag veya rev ile commit seçilebilir
regex = { git = "https://github.com/rust-lang/regex.git", tag = "1.10.3" }
# yerel klasör; yayımlarken crates.io sürümü kullanılsın diye version da verilebilir
hello_utils = { path = "hello_utils", version = "0.1.0" }
# özellik seçimi ve varsayılan özellikleri kapatma
flate2 = { version = "1.0.3", default-features = false, features = ["zlib-rs"] }
# yeniden adlandırma: kodda bar olarak kullanılır, gerçek paket foo
bar = { version = "0.1", package = "foo" }

[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"

[dev-dependencies]
tempdir = "0.3"

[build-dependencies]
cc = "1.0.3"
  • crates.io: yalnızca ad ve sürüm gereksinimi yeterlidir. Cargo varsayılan olarak bağımlılıkları crates.io üzerinde arar.
  • git: yalnızca depo adresi verirseniz varsayılan dalın son commit'i kullanılır. branch, tag veya rev ile belirli bir commit seçebilirsiniz. Cargo, git bağımlılığının commit'ini eklendiği anda Cargo.lock dosyasına kilitler ve yalnızca cargo update çalıştırdığınızda güncelleme arar.
  • path: aynı depodaki alt crate'ler için kullanılır. Yol, bağımlılığın Cargo.toml dosyasının bulunduğu klasörü tam olarak göstermelidir; Cargo git'te olduğu gibi klasör ağacında arama yapmaz. Yalnızca path ile belirtilmiş bağımlılığa sahip paketler crates.io'ya yayımlanamaz; path ile birlikte version verirseniz yerelde klasör, yayımlandığında kayıttaki sürüm kullanılır.
  • Platforma özel: [target.'cfg(windows)'.dependencies] gibi bir başlık altında yazılır ve yalnızca o platformda derlenir.
  • dev-dependencies: yalnızca testler, örnekler ve benchmark'lar derlenirken kullanılır ve sizin paketinize bağlı başka paketlere aktarılmaz.
  • build-dependencies: derleme betiği (build.rs) içindir. Derleme betiği normal bağımlılıklara erişemez, paket de derleme bağımlılıklarına erişemez; ikisi ayrı derlenir.
  • package anahtarı: bir crate'i kodda farklı bir adla kullanmanızı sağlar; örneğin aynı crate'in iki farklı sürümüne bağlanmak için.

Sürüm gereksinimi söz dizimi

time = "0.1.12" yazdığınızda tam olarak 0.1.12 sürümünü değil, bir sürüm aralığını istemiş olursunuz. Varsayılan gereksinim, en soldaki sıfır olmayan bileşen aynı kaldığı sürece güncellemeye izin verir. Bu, sıfırla başlayan sürümlerde SemVer'den farklı ve daha katı bir yorumdur: 0.2.3 gereksinimi 0.3.0'a geçişe izin vermez.

YazımAnlamıNot
1.2.3 veya ^1.2.3>=1.2.3, <2.0.0Varsayılan (caret); belge şapkasız yazımı önerir
0.2.3>=0.2.3, <0.3.0En soldaki sıfır olmayan bileşen sabit
0.0.3>=0.0.3, <0.0.4Yalnızca aynı yama sürümü
~1.2.3>=1.2.3, <1.3.0Tilde: yalnızca yama güncellemeleri
1.*>=1.0.0, <2.0.0Joker; crates.io çıplak * kabul etmez
>= 1.2, < 1.5İki koşul birlikteVirgülle ayrılan tüm koşullar sağlanmalı
= 1.2.3Yalnızca 1.2.3Karşılaştırma gereksinimi

Belge, emin olmadığınızda varsayılan operatörü kullanmanızı ve üst sınırı bir sonraki uyumsuz sürümden daha aşağıda tutmaktan (örneğin ~2.0 ya da 2.0.*) kaçınmanızı önerir; aksi halde bağımlılık ağacındaki başka bir paket daha yeni bir sürüm istediğinde çözülemeyen bir çakışma oluşabilir. Ön sürümler (1.0.0-alpha gibi) siz açıkça istemedikçe eşleşmez. Belirli bir sürümü sabitlemek istiyorsanız bunu çoğu zaman manifest yerine Cargo.lock ile yapmak daha uygundur.

Cargo.lock: tekrarlanabilir derleme

The Cargo Book farkı kısaca şöyle özetler: Cargo.toml bağımlılıklarınızı geniş anlamda tarif eder ve onu siz yazarsınız; Cargo.lock ise bağımlılıklar hakkında kesin bilgiyi içerir, Cargo tarafından tutulur ve elle düzenlenmemelidir. Örneğin bir git bağımlılığında commit belirtmezseniz, bugün ve yarın yapılan iki derleme farklı commit'leri kullanabilir. Lock dosyası ilk derlemede kullanılan tam commit'i ya da sürümü kaydettiği için projeyi alan herkes aynı sürümlerle derler. Yeni sürümlere geçmeye hazır olduğunuzda cargo update tüm bağımlılıkları, cargo update regex yalnızca birini günceller. Belgenin önerisi açıktır: emin değilseniz Cargo.lock dosyasını sürüm kontrolüne (örneğin Git) ekleyin.

Çalışan örnek: iki paketli workspace ve path bağımlılığı

Şimdi bunları küçük bir projede birleştirelim. cargo new --vcs none --lib fiyat ve cargo new --vcs none magaza ile iki paket oluşturduk, üstlerine bir kök manifest ekledik. Klasör yapısı şöyle:

bash
calisma/
  Cargo.toml          # [workspace] kök manifest
  Cargo.lock          # ilk derlemede Cargo oluşturur, tüm üyeler paylaşır
  fiyat/
    Cargo.toml
    src/lib.rs
  magaza/
    Cargo.toml
    src/main.rs
  target/             # ortak çıktı klasörü

Kök manifestte [package] yok, yalnızca [workspace] var; buna sanal manifest (virtual manifest) denir. Ortak sürüm ve edition değerleri [workspace.package] altında bir kez tanımlanıyor, profiller de yalnızca burada yazılıyor.

toml
[workspace]
members = ["fiyat", "magaza"]
resolver = "3"

[workspace.package]
version = "0.2.0"
edition = "2024"

[profile.release]
lto = true
codegen-units = 1

[profile.hizli]
inherits = "release"
lto = false

fiyat kütüphane paketi sürüm ve edition değerlerini version.workspace = true ile devralıyor ve isteğe bağlı bir kdv özelliği tanımlıyor:

toml
[package]
name = "fiyat"
version.workspace = true
edition.workspace = true

[features]
default = []
kdv = []
rust
/// Kuruş cinsinden fiyatı TL biçiminde yazar.
pub fn bicimle(kurus: u64) -> String {
    format!("{},{:02} TL", kurus / 100, kurus % 100)
}

/// `kdv` özelliği açıksa %20 KDV ekler, kapalıysa fiyatı değiştirmez.
pub fn son_fiyat(kurus: u64) -> u64 {
    if cfg!(feature = "kdv") {
        kurus * 120 / 100
    } else {
        kurus
    }
}

magaza ikili paketi, fiyat paketine path ile bağlanıyor ve kütüphanenin fonksiyonlarını fiyat:: yoluyla çağırıyor. cfg!(debug_assertions) hangi profille derlendiğini göstermek için kullanılıyor.

toml
[package]
name = "magaza"
version.workspace = true
edition.workspace = true

[dependencies]
fiyat = { path = "../fiyat" }
rust
fn main() {
    let kurus = 12_550;
    let son = fiyat::son_fiyat(kurus);
    println!("ürün: {}", fiyat::bicimle(son));
    println!("profil: {}", if cfg!(debug_assertions) { "debug" } else { "optimize" });
}

Kök klasörde çalıştırdığımız komutlar ve çıktıları aşağıda. -q bayrağı Cargo'nun derleme satırlarını gizleyip yalnızca programın çıktısını bırakıyor; -p ise workspace içindeki paketi seçiyor.

bash
cargo run -q -p magaza
# çıktı: ürün: 125,50 TL
# çıktı: profil: debug

cargo run -q -p magaza --features fiyat/kdv
# çıktı: ürün: 150,60 TL
# çıktı: profil: debug

cargo run -q -p magaza --release
# çıktı: ürün: 125,50 TL
# çıktı: profil: optimize

cargo run -q -p magaza --features kdv
# çıktı: error: the package 'magaza' does not contain this feature: kdv
# çıktı: help: package with the missing feature: fiyat

Son komut önemli bir kuralı gösteriyor: özellikler onları tanımlayan pakete aittir. Workspace içinde başka bir üyenin özelliğini komut satırından açmak için paket/özellik yazımı gerekir; Cargo hata mesajında özelliğin hangi pakette olduğunu da söylüyor. Derlemeden sonra kökte tek bir Cargo.lock oluştu ve içinde magaza paketinin fiyat paketine bağlı olduğu kayıtlı. Modüller ve pub görünürlüğü için Rust modül sistemi yazısına bakın.

Features: koşullu derleme ve isteğe bağlı bağımlılıklar

Features: koşullu derleme ve isteğe bağlı bağımlılıklar: Komut satırı, Bağımlılıkta, İsteğe bağlı bağımlılık, Bağımlılığın özelliği

Features, [features] tablosunda tanımlanan adlandırılmış seçeneklerdir. Her özellik, açıldığında başka hangi özellikleri ya da isteğe bağlı bağımlılıkları açacağını bir dizi olarak listeler; kdv = [] hiçbir şeyi açmayan bir özelliktir. Cargo açık özellikleri derleyiciye --cfg bayrağıyla iletir, kodda da #[cfg(feature = "kdv")] özniteliği ya da örnekteki gibi cfg! makrosuyla sınanır. Varsayılan olarak tüm özellikler kapalıdır; default özelliği ile varsayılan seti değiştirebilirsiniz.

  • Komut satırı: --features listelenen özellikleri açar, --all-features seçilen paketlerin tüm özelliklerini açar, --no-default-features varsayılan özelliği kapatır.
  • Bağımlılıkta: features = ["..."] ile bağımlılığın özelliklerini açarsınız, default-features = false ile varsayılanlarını kapatırsınız. Örneğimizde fiyat = { path = "../fiyat", features = ["kdv"] } yazsaydık KDV her zaman açık olurdu.
  • İsteğe bağlı bağımlılık: optional = true ile işaretlenen bağımlılık varsayılan olarak derlenmez ve aynı adla örtük bir özellik oluşturur. Özellik tablosunun herhangi bir yerinde dep:ad yazarsanız örtük özellik oluşmaz; belgeye göre bu söz dizimi Rust 1.60 ile gelmiştir.
  • Bağımlılığın özelliği: "paket/özellik" yazımı bağımlılık isteğe bağlıysa onu da açar; "paket?/özellik" ise yalnızca bağımlılık başka bir nedenle zaten açıksa özelliği etkinleştirir.

Features konusunda en önemli kavram birleştirmedir (unification): bir bağımlılık ağaçta birden çok paket tarafından kullanılıyorsa Cargo onu, tüm paketlerin açtığı özelliklerin birleşimiyle bir kez derler. Bu yüzden belge özelliklerin eklemeli (additive) olmasını ister: bir özelliği açmak işlevsellik kaldırmamalı ve özelliklerin herhangi bir birleşimi güvenle açılabilmelidir. Bu kuralın tipik örneği, no_std desteği için no_std adlı bir özellik yerine std'yi açan bir std özelliği tanımlamaktır. Aynı sebeple default-features = false yazmanız varsayılanların kapalı kalacağını garanti etmez; ağaçtaki başka bir paket aynı bağımlılığı varsayılanlarla istiyorsa varsayılanlar açılır.

Profiller: dev, release ve özel profiller

Profiller: dev, release ve özel profiller: opt-level, debug, debug-assertions, overflow-checks

Cargo'nun dört yerleşik profili vardır: dev, release, test ve bench. Profil, çalıştırılan komuta göre otomatik seçilir: cargo build, cargo run ve cargo check için dev, cargo test için test, cargo bench için bench, cargo install için release. --release bayrağı --profile=release ile eşdeğerdir. Varsayılan değerlerden en çok merak edilenler şunlar:

Ayardevrelease
opt-level03
debugtruefalse
debug-assertionstruefalse
overflow-checkstruefalse
incrementaltruefalse
codegen-units25616
ltofalsefalse

Tablodaki overflow-checks satırı pratikte önemlidir: dev profilinde tamsayı taşması panik üretirken release profilinde varsayılan olarak denetlenmez. Taşma davranışının ayrıntısı Rust veri tipleri yazısında. Kendi profilinizi tanımlarken inherits anahtarı zorunludur; belirtmediğiniz ayarlar o profilden alınır. Örnek workspace'imizde hizli profili release ayarlarını devralıp LTO'yu kapatıyor. Belgeye göre her profilin çıktısı target altında profil adıyla aynı klasöre yazılır:

bash
cargo build -p magaza --profile hizli
# çıktı (ilgili satır): Finished `hizli` profile [optimized] target(s) in 0.24s

ls target
# çıktı: CACHEDIR.TAG
# çıktı: debug
# çıktı: hizli
# çıktı: release

Profiller konusunda gözden kaçan kural şudur: Cargo yalnızca workspace kökündeki manifestin profil ayarlarına bakar; bağımlılıklarda ya da üye paketlerde yazılan [profile] bölümleri yok sayılır. Ayrıca bu sürümün belgesi, hata ayıklayıcıyla çalıştırılacak ikili dosyalar için dev profilini devralan bir debug profili de listeler ve cargo test sayfası bir testi hata ayıklayıcıda incelemek için --profile=debug kullanılabileceğini söyler.

Workspace ayrıntıları ve resolver

Workspace, birlikte yönetilen bir ya da daha fazla paketten oluşur. Belgedeki temel noktalar: cargo check --workspace gibi komutlar tüm üyelerde çalışabilir, bütün paketler kökteki tek Cargo.lock dosyasını ve varsayılan olarak kökteki target klasörünü paylaşır, [patch], [replace] ve [profile.*] bölümleri yalnızca kök manifestte geçerlidir. members listesinde crates/* gibi glob kalıpları kullanılabilir; belge üyeleri düz bir crates/ klasöründe tutup glob kullanmayı önerir. Workspace klasöründeki path bağımlılıkları da otomatik olarak üye olur.

[workspace.package] tablosu sürüm, edition, lisans ve yazarlar gibi alanların üyelere alan.workspace = true ile devredilmesini sağlar; [workspace.dependencies] ise ortak bağımlılık tanımlarını bir kez yazıp üyelerde workspace = true ile kullanmanızı sağlar. Bu tabloda optional kullanılamaz ve üyenin eklediği features değerleri workspace tanımına eklenir. Belgeye göre edition 2024 paketlerinde ve Rust 1.99 ile, üye paketteki default-features değeri workspace tanımındaki değeri geçersiz kılar; daha eski sürümlerde ya da eski edition'larda paket seviyesindeki default-features = false yok sayılabilir veya reddedilebilir. Cargo sürümlerindeki diğer değişiklikleri Cargo son sürüm değişiklikleri yazısında topladık.

Edition Guide'a göre edition = "2024" yazmak resolver = "3" anlamına gelir ve bu çözümleyici bağımlılık sürümlerini seçerken paketlerin rust-version değerini dikkate alır. Çözümleyici workspace genelinde bir ayardır ve yalnızca en üst paketten okunur. Sanal manifestte bakılacak bir package.edition olmadığı için resolver değerini örneğimizdeki gibi açıkça yazmanız gerekir.

Sık yapılan hatalar

  • edition satırını unutmak: elle yazılan manifestte edition yoksa paket 2015 edition ile derlenir ve async gibi anahtar kelimeler beklenmedik hatalar verir.
  • Sürümü tam sabitlediğini sanmak: "1.2.3" yazımı 1.x serisindeki daha yeni sürümlere izin verir; tam sabitleme için Cargo.lock ya da =1.2.3 kullanılır.
  • Üye paketin profil ayarlarını değiştirmek: workspace kökü dışında yazılan profiller yok sayılır.
  • Dışlayıcı (mutually exclusive) özellikler tasarlamak: birleştirme yüzünden iki özellik aynı anda açılabilir. Belge bundan kaçınmayı, mümkün değilse compile_error! ile bu durumu derleme anında yakalamayı önerir.
  • Cargo.lock dosyasını elle düzenlemek: değişiklikler için cargo update kullanın.
  • Sanal workspace'te resolver yazmamak: edition'dan çıkarım yapılamadığı için değeri açıkça belirtin.

Sık Sorulan Sorular

Cargo.lock dosyasını Git'e eklemeli miyim?

The Cargo Book'un önerisi, emin değilseniz eklemenizdir. Böylece projeyi derleyen herkes aynı bağımlılık sürümlerini kullanır. Sürekli entegrasyonda bağımlılıkların en yeni sürümlerini de ayrıca denemek iyi bir pratiktir; CI kurulumu için GitHub Actions CI/CD rehberi yazısına bakabilirsiniz.

cargo add ile elle yazmak arasında fark var mı?

Sonuç aynı manifest satırıdır. Rust version belgesine göre cargo add, bağımlılığın sürüm gereksinimini paketinizin rust-version değeriyle uyumlu en yeni sürüme göre seçer ve bu en yeni sürüm değilse sizi bilgilendirir.

Workspace mi yoksa ayrı depolar mı?

Birlikte geliştirilen ve aynı bağımlılık sürümlerini paylaşması gereken paketler için workspace pratiktir: tek lock dosyası, tek target klasörü ve tek komutla hepsini test etme imkânı verir. Bağımsız sürümlenen, farklı ekiplerin bakımını yaptığı paketler için ayrı depolar daha uygun olabilir. Kitabın workspace bölümü de konuyu birden çok paketli küçük bir örnek üzerinden anlatır.

Derlenmiş programı sunucuda nasıl çalıştırırım?

cargo build --release çıktısı target/release altındaki tek bir ikili dosyadır. Bunu bir servis olarak çalıştırmak için systemd servis yönetimi, konteyner içinde dağıtmak için Docker ile uygulama deploy yazılarına bakabilirsiniz.

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 kurulumu · Rust test yazma · Rust modül sistemi · Cargo son sürüm değişiklikleri · Clippy ve rustfmt