rust_library kaynakları için düz dosya düzeni kullanın [chrome]
use ifadesi tek bir modülden öğe import etmelidir
#[derive()] kullanın
dyn Trait
From impl'lerini açık kurucu metotlarla eşleştirin
Default uygulamalıdır
#[doc(hidden)] olarak işaretlenmiş öğelere erişmeyin
const fonksiyonları sorumlu bir şekilde kullanın
#[non_exhaustive] uygulayın
#[non_exhaustive] uygulayın
#[must_use]
#[must_use]
main() öncesinde kod çalıştırmayın
unwrap yerine expect tercih edin
non_exhaustive enum'larda desenleri atlamayın
panic::catch_unwind çağırmayın
format! / write! tercih edin
Bu belge, Rust programlama dilinin Google'daki kullanımına yönelik bir stil rehberidir. Bu bağlamda "stil", yalnızca biçimlendirmeyi değil, API ve uygulama tasarımına ilişkin yönergeleri de kapsar.
Rust; google3, Android, ChromeOS, Fuchsia ve Chrome dâhil olmak üzere birçok yerde kullanılıyor ve Google'daki Rust kullanımının hem kod tabanları içinde hem de kod tabanları arasında tutarlı olmasını istiyoruz. Stil yönergeleri, Rust kodumuza tutarlılık kazandırmanın yanı sıra bir kalite standardı oluşturmaya da yardımcı olur.
Bu rehberdeki kuralların hâlihazırdaki geçerlilik kapsamı:
Genel olarak bu belgeyi C++ stil rehberine sahip olmamızla aynı nedenlerle tutuyoruz.
Stil kurallarının çoğu aşağıda sıralanan amaçlardan bir veya birkaçından doğar.
Bu stil rehberiyle çelişmediği ölçüde resmî, herkese açık Rust stil kurallarına uyun.
Bir "çelişki", bu stil rehberinin Rust stil rehberinde önerilmeyen veya Rust stil rehberinin öncelikli olarak teşvik ettiği kalıba aykırı bir kalıp önerdiği durumdur.
Bu stil rehberindeki yönergeler neden olağan Rust stil kurallarından sapıyor?
Stil rehberlerinin çoğunun üç amacı vardır: tutarlılık, okunabilirlik ve iyi API tasarımını teşvik etmek. Hangi seçimlerin yapıldığından bağımsız olarak, bu seçimlerin önceden yapılmış olması herkesin işini kolaylaştırır ve gereksiz tartışmaları (bikeshedding) sınırlar.
Bu stil rehberi de bu amaçları paylaşıyor; ancak sık görülmeyen ek bir amacı da var: Büyük Ölçekli Değişikliklerin (LSC'lerin) yolunu açmak. Google'daki kod tabanlarının çoğu gibi büyük kod tabanlarının zaman zaman araçlar tarafından desteklenen, kod tabanının tamamına yayılan refaktörlere ihtiyacı olur: bunun iyi çalışması için kodun, araçların kolayca işleyebileceği bir biçimde olması gerekir. Tutarlılık buna yardımcı olur; bilinçli olarak refaktöre uygun kalıplar kullanmak da öyle.
API tasarımıyla ilgili kuralları uygularken iki tür kodu ayırt etmelisiniz:
Kütüphane kodu
Uygulama kodu
Kütüphane kodu için API tasarımına ilişkin stil rehberi kuralları bağlayıcıdır.
Uygulama kodu için API tasarımı kuralları güçlü bir öneridir. Bunları uygularken unutmayın: uygulama kodu belirli bir problemi çözmek için yazıldığından, geniş yeniden kullanılabilirlik yerine genellikle basitliği ve doğrudanlığı tercih etmelidir. Erken genelleştirme ve soyutlamadan kaçının; acil ihtiyacı karşılamak için gerekenlere odaklanın.
Terminoloji:
Crate, bir derleme birimidir. Bir crate her zaman isimsiz bir Rust modülü olan "kök modül"ü (root module) içerir.
Rust modülü, bir crate içindeki kod organizasyonu birimidir. Rust modülleri, adların görünürlüğünü denetlemek için kullanılır. Modüller iç içe geçebilir. Ayrıca kodu dosyalar ve dizinler arasında bölmek için de kullanılabilirler.
Aşağıdaki bölümler, Chromium'da modülleri dosyalara bölmenin tüm olanaklarından yararlanmamanın tercih edildiğini anlatır.
Chromium'da küçük crate'leri tercih edin; ideal olarak –ama zorunlu değil– tek dosya (tek bir soyutlamaya odaklanmış). Kodunuz küçük crate'lere bölünebiliyorsa bölün.
Bir crate, bir veya daha fazla .rs dosyası içerebilen tek bir
rust_static_library (chrome) kuralına karşılık gelir.
Ek hususlar:
Kök modülün, herkese açık API'leri yaprak alt modüllerden yeniden
dışa aktarmasına (re-export) izin verilir; böylece alt modüller crate'in
bir uygulama ayrıntısı hâline gelir (kullanıcıların bu alt modülleri
bilmesi gerekmez).
Küçük crate'lerle ilgili bu kural, alt modülü olan ve olmayan crate'ler için eşit derecede geçerlidir. Önce API'lerin aynı crate içinde olması gerekip gerekmediğini değerlendirin; ancak ondan sonra alt modüllerin API'leri düzenlemeye yardımcı olup olamayacağını düşünün.
Alt modül kullanmaya karar verirseniz beklenen dosya yapısına uyun.
Trait impl tutarlılık kuralları birden fazla tür ve trait'in tek bir kütüphanede yer almasını zorunlu kıldığında veya büyük bir temel kütüphane oluşturuyorsanız daha büyük crate'lere izin verilir.
Gerekçe
Kod tabanımızın devasa boyutu, derleme grafiğine bağımlılık eklemenin yerel olmayan etkileri ve paralel derleme ile derleme önbelleğe alma performansını iyileştirmek nedeniyle Chromium'da ince taneli kütüphane hedefleri önerilir.
rust_library kaynakları için düz dosya düzeni kullanın [chrome]Chromium'da tek bir rust_library'ye (tek bir Rust crate'ine) ait .rs kaynak
dosyaları tek bir dizinde bulunmalıdır.
Tek bir crate'e ait dosyaları düzenlemek için alt dizinler kullanmayın. Bir crate'in dosyalarını alt dizinlere ayırmak gerekli görünüyorsa bu, crate'in çok büyüdüğünün ve bölünmesi gerektiğinin güçlü bir göstergesidir.
Bu crate düzeni, açık kaynak depolarında yaygın olarak kullanılan geleneksel Cargo dizin yapısından bilinçli olarak farklıdır.
İstisna: //third_party içinde olup aynı zamanda
Cargo ile derlenen Rust kodu, geleneksel
Cargo dizin yapısını izlemelidir.
Gerekçe
Chromium'daki yaklaşım şu etkenlerden kaynaklanır:
rust_library
hedefi yalnızca bir kaynak dosya içerecektir.rust_library hedefi için bir dizin
oluşturmak, çoğu dizinin yalnızca tek bir kaynak dosya içermesiyle
sonuçlanırdı.rust_library hedefleri de alt modüller için ayrı dizinler
oluşturmamalıdır; bu, tutarlı bir yaklaşım sağlar.Bir yaprak modülün birincil kullanıcı tanımlı öğesi olduğunda (ör. Foo),
modül bu öğeye göre adlandırılmalıdır (ör. mod foo ve foo.rs).
Belirli bir soyutlamayla birincil olarak ilgili olmayan yaprak modüller istenildiği gibi adlandırılabilir.
Gerekçe
Bu, bir kaynak dosyanın veya yaprak modülün içeriğini/amacını bir bakışta tanımlamayı kolaylaştırır.
Yaprak olmayan modüllerdeki tek öğeler modül bildirimleri (tanımlar değil) ve
yeniden dışa aktarımlar olmalıdır.
Yaprak modüllerin tanımının şu alt modüllere izin verdiğini unutmayın:
mod test ve #[cxx::bridge] gibi diller arası bağlama modülleri.
İstisnalar:
Gerekçe
Bu, bir şeyleri bulmayı kolaylaştırır ve yapı ile içeriğin karışmasını önler.
Glob import kullanmayın; ancak kendi takdirinize bağlı olarak isteğe bağlı şekilde kullanabileceğiniz şu durumlar hariç:
use super::*use enum::*pub use submodule::*C++ birlikte çalışabilirliği: Aynı ad alanı (C++'ta) farklı Rust crate'lerinde (Rust'ta) bir alt modül olduğundan, ad alanlarını glob import'lar kullanarak özel bir kopyada birleştirebilirsiniz. Örneğin:
rust
mod absl {
pub use status::absl::*;
pub use strings::absl::*;
}
Bu varsayımsal örnekte, absl::Status ve absl::StrCat'i Rust'tan,
C++'taki gibi kullanabilirsiniz; bunları status::absl::Status ve
strings::absl::StrCat şeklinde yazmanız gerekmez.
Bu yalnızca C++ ad alanlarını birleştirmek için kullanılmalıdır ve ortaya çıkan birleşik modül herkese açık olmamalıdır.
Prelude'u teşvik eden üçüncü taraf crate'ler kullanılıyorsa buna izin vermek inceleyicinin takdirine bağlıdır.
Bu ayrıca prelude tanımlamaktan kaçınılması gerektiği anlamına gelir.
Gerekçe
Glob import'lar, LSC çalıştıran kod temizlikçileri için angaryayı artırır ve xref'lerin bulunmadığı bağlamlarda bir türün nereden geldiğini bilmeyi zorlaştırır.
use ifadesi tek bir modülden öğe import etmelidirBu kural, imports_granularity = Module rustfmt ayarına karşılık gelir.
Temel olarak, bir use ifadesinin {..} bloğunun içinde hiçbir ::
bulunmamalıdır. Örneğin:
use foo::Foo;
use foo::bar::Bar;
use foo::bar::Baz;
use foo::Foo;
use foo::bar::{Bar, Baz};
use foo::{Foo, bar::Bar};
Trait'in metotları dosya boyunca yoğun olarak kullanılmadığı sürece trait'leri fonksiyon gövdeleri içinde import edin.
fn foo(s: &str) -> Result<u8, std::num::ParseIntError> {
// Trait'i modül kapsamında değil, fonksiyon içinde import edin.
use std::str::FromStr;
u8::from_str(s)
}
Başka modüllerde veya crate'lerde tanımlanmış üst düzey fonksiyonları kullanmak
için modüllerini import edin ve onları modulename::functionname() şeklinde
çağırın. Örneğin:
use std::mem;
let x = mem::transmute(y);
use std::mem::transmute;
let x = transmute(y);
İstisnalar:
Chromium'da küçük, tek amaçlı birinci taraf crate'lerden fonksiyon import ederken.
Modül adının kendisi çakışmaya eğilimli olduğunda. Örneğin modül adı bir C++ ad alanı adı olduğunda ve birden fazla crate aynı alt modülleri tanımladığında.
Gerekçe
Pek çok Rust kod tabanı, nitelenmemiş küçük harfli adların yerel kabul
edildiği bir stili tutarlı biçimde korur; yani foo() fonksiyonunun aynı
modülden geldiği varsayılabilir. Bu kural büyük ölçüde inceleyiciler arasındaki
anlaşmazlıkları önlemek için vardır; faydası çoğunlukla tutarlılık ve kodu
xref'ler olmadan okumada küçük bir avantajdır.
Varsayılan olarak türler ve trait'ler, kodda modül yolları kullanılmadan
belirtilmelidir; örneğin kod regex::Regex değil, dosyanın başında veya
gerektiğinde ilgili fonksiyonun içinde use regex::Regex ile birlikte Regex
kullanmalıdır.
İstisnalar:
io::Error gibi yaygın adlara sahip
türleri doğrudan belirtmek kabul edilebilir. Yeniden adlandırmak da kabul
edilebilir (use std::io::Error as IoError).rustc içindeki hir modülü.use gerektirmenin verimsiz olduğu yerlerde.Gerekçe
Bu kural büyük ölçüde inceleyiciler arasındaki anlaşmazlıkları önlemek için vardır.
chromium::import!() kullanın.Uygun biçimlendirme aracını kullanın (ör. Chromium'da git cl format).
Yapılandırma (stil rehberi yazarları için)
Bir kod tabanı, kararlı (stable) Rust üzerinde derlemeyi önemsiyorsa ve ilgili
seçenek kullandığı kararlı Rust sürümünde mevcut değilse şu rustfmt
seçeneklerini uygulamama tercihinde bulunabilir:
Satır başına en fazla 100 sütun:
max_width = 100
Onaltılık literallerde küçük harfli alfabe kullanımı (0xAB123CDEF yerine
0xab123cdef):
hex_literal_case = Lower
Makro eşleştirici biçimlendirmesi:
format_macro_matchers = true
Literallerde görünmez karakterler için kaçış dizileri kullanımı. Örneğin
sıfır genişlikli birleştirici (Zero-Width joiner) içeren metin istiyorsanız
\u{200D} kullanın.
#[rustfmt::skip] kullanarak otomatik biçimlendirmeyi devre dışı
bırakabilirsiniz; ancak yalnızca şu durumlarda:
Önemli: Kişisel stil tercihleri için biçimlendirme atlanmamalıdır.
Rc ve Arc'tan kaçınınBellek yönetimi için Rc ve Arc yerine benzersiz sahipliği ve ödünç almayı
tercih edin.
İstisna:
Rc ve Arc
kullanın. Bu, kod incelemesi sırasında tartışılacak bir takdir meselesidir.Gerekçe
Genel olarak Rust, nesne grafiklerini paylaşımlı sahiplik akıllı işaretçileri
kullanmadan temsil etmeye izin verir. Bu işaretçiler çalışma zamanı yükü ve
kendilerine özgü karmaşıklık getirir. Başka dillerden gelen mühendisler Rc ve
Arc'ı çok hızlı kullanmaya eğilimli olabilir.
Bir nesnenin gerçekten derin kopyasına ihtiyaç duyduğunuzda .clone()
kullanın.
.clone()'ı ödünç almanın yerine geçen bir çözüm olarak kullanmaktan kaçının.
Rust'ın sahiplik ve ödünç alma kurallarıyla doğal biçimde çalışan kod
tasarlayın. Ancak ödünç alma ilişkileri aşırı karmaşık hâle gelirse .clone()
pragmatik bir seçim olabilir.
Klonlama ile ödünç alma arasında seçim yaparken kodun performans gereksinimlerini göz önünde bulundurun.
Not: Bu tavsiye Rc veya Arc işaretçilerinin klonlanması için geçerli
değildir. Referans sayımlı işaretçilerin klonlanması derin kopya oluşturmaz ve
onları kullanmanın beklenen bir yoludur.
Gerekçe
Derin kopyalar, bellek tahsisi ve veri kopyalama nedeniyle pahalı olabilir.
Fonksiyonlar ödünç alınmış, değişmez veri kabul ettiğinde, en fazla esnekliği sağlayan referans türünü kullanın. Akıllı işaretçilere, kapsayıcılara veya sarmalayıcılara referanslar yerine verinin kendisine referans geçin.
| Kullanın | Yerine | Neden? |
|---|---|---|
&T |
&Box<T> |
Box'ın içindeki veriyi ödünç alın |
&[T] |
&Vec<T> |
Belirli kapsayıcılar yerine herhangi bir dilimi kabul edin |
&str |
&String |
Sahiplenilmiş string'lere referans yerine string dilimi kabul edin |
Option<&T> |
&Option<T> |
Option'ın içindeki veriyi ödünç alın |
İstisnalar:
Bağlam için Clippy lint'lerine bakın:
borrowed_box
ve
ptr_arg.
Gerekçe
Belirli akıllı işaretçileri, kapsayıcıları veya sarmalayıcıları anmayan referans türlerini kabul etmek fonksiyonları daha çok yönlü ve performanslı kılar: çağıranlar, veriyi önce kopyalamadan çeşitli biçimlerde tutulan veriyi geçebilir.
Mümkün olduğunda Vec<T> döndürmek yerine iterator (somut türler veya
impl Iterator) döndürmeyi tercih edin.
Not: Uygulama kodunda, kodu basitleştiriyorsa ve iterator'ların esneklik ile
performans avantajları açıkça gerekli değilse Vec<T> döndürmek sorun değildir.
İstisnalar:
for döngüsü olarak daha iyi çalışıyorsa ve zorunlu tahsis
çok da önemli değilse.Gerekçe
Yüksek düzeyde yeniden kullanılabilir kütüphane kodu genellikle daha esnek ve performanslı API'ler sunmaya çalışmalıdır. Iterator döndürmek, çağıranların veriyi nasıl tüketeceğini seçmesine olanak tanır: bir vektöre toplamak, bir döngüde işlemek veya başka iterator adaptörleriyle zincirlemek. Bu seçeneklerden bazıları kullanıcıların gereksiz tahsislerden kaçınmasını sağlar.
Uygulama kodunda bir fonksiyonun kullanım kalıpları genellikle daha kolay öngörülür. Performansın sorun olmadığı veya verinin zaten bir vektöre toplanacağı açıksa, vektör döndürmek daha basit bir API sunar.
Yüksek düzeyde yeniden kullanılabilir kütüphane kodu, girdi üzerinde yalnızca
iterasyon yapması gerekiyorsa iterator veya impl IntoIterator kabul etmelidir.
Not: Uygulama kodunda, kodu basitleştiriyorsa ve iterator'ların esneklik ile
performans avantajları açıkça gerekli değilse &[T] dilimini kabul etmek sorun
değildir.
Gerekçe
Yüksek düzeyde yeniden kullanılabilir kütüphane kodu genellikle daha esnek ve performanslı API'ler sunmaya çalışmalıdır. Iterator kabul eden fonksiyonlar çağıranlara şunları sunar:
Uygulama kodunda bir fonksiyonun kullanım kalıpları genellikle daha kolay öngörülür. Performansın sorun olmadığı veya çağıranların zaten her zaman bir vektöre sahip olacağı açıksa, dilim kabul etmek daha basit bir API sunar.
Into kabul etmekBir T kabul eden kütüphane API'leri tasarlarken kullanıcıların çeşitli
türlerde değerlere sahip olması muhtemelse, çağrı yerindeki ergonomi her
şeyden önemliyse T yerine impl Into<T> kabul etmeyi düşünün.
İpucu: Bu, kod şişkinliği kaygılarıyla dengelenmelidir; kodunuzda çok sayıda
Into<T> kullanmak bir zorunluluk değildir.
Not: Uygulama kodu neredeyse hiçbir zaman impl Into<T> parametreleri
kullanmamalıdır.
Gerekçe
impl Into<T> kullanmak, bir fonksiyonun çeşitli uyumlu türlerdeki çeşitli
argümanları doğrudan kabul etmesini sağlar. Örneğin impl Into<String> kabul
eden bir fonksiyon, doğrudan bir String veya bir &str literali ile
çağrılabilir.
Buradaki ödünleşim, çağrı yerinde .into() yazmaktan kaçınmanın kolaylığı ile
generic'lerin maliyeti (daha karmaşık API imzası, artan kod boyutu, bazen tür
çıkarımı zorlukları) arasında denge kurmaktır.
bool yerine enum tercih edinParametrenin amacının açık olmadığı durumlarda bool yerine enum fonksiyon
parametreleri kullanın.
Yine de bool kullanıyorsanız, argüman listesindeki bool'un amacını
belgelemek için satır içi bir yorum kullanmayı düşünün.
API sözleşmenize uyan hata raporlama mekanizmasını seçin: Option, Result
veya panic!.
Basit başarısızlıklar için Option kullanın: nedeni açıksa ve çağıran daha
fazla ayrıntıdan fayda görmeyecekse. Bu, eksik bir değer veya ayrıştırma
uyuşmazlığı gibi standart, öngörülen sonuçlar için en iyisidir.
// İyi: Başarısızlığın nedeni açık (tamsayı taşması). Makul biçimde daha
// fazla ayrıntı sağlanamazdı.
fn checked_add(a: u32, b: u32) -> Option<u32> { ... }
Çağıranın bağlama ihtiyaç duyduğu kurtarılabilir başarısızlıklar için
Result kullanın. Bunlar genellikle dış etkenlerden (G/Ç veya ağ gibi)
kaynaklanan ve doğru uygulanmış kodun bile önceden yapılan kontrollerle
engelleyemeyeceği "kaçınılmaz" hatalardır. Yine de çağıran, hata türlerini
makul biçimde öngörebilir (dosyayla ilgili bir fonksiyon G/Ç hatasıyla
başarısız olabilir). Hata ayrıntıları, çağıranın başarısızlık kiplerini ayırt
etmesine ve uygun şekilde kurtarmasına olanak tanır.
// İyi: Çağıranın *neden* başarısız olduğunu bilmesi gerekir (NotFound veya
// PermissionDenied).
fn open_file(path: &Path) -> Result<File, io::Error> { ... }
Hataları ve ihlal edilen değişmezleri (invariant) raporlamak için panic!
kullanın. Bu hatalar önlenebilirdir: programda bir kontrol eksiktir ve bir
ön koşulu yerine getirmeden bir işlemi çağırmaktadır. Bu kusurlar çalışma
zamanında ele alınmak yerine kodda düzeltilmelidir; yine de catch_unwind ile
kaba taneli bir kurtarma mümkün olabilir.
// İyi: Değişmez (indeks sınırlar içinde olmalı) ihlal edilirse panikler.
// Bu, çağıranın mantığındaki bir hataya işaret eder.
impl<T> MyVec<T> {
pub fn remove(&mut self, index: usize) -> T {
if index >= self.len() {
panic!("index out of bounds");
}
// ...
}
}
Not: Bazı kod tabanları paniklerden tamamen kaçınmayı ve bozuk değişmezlerin
bile Result aracılığıyla raporlanmasını zorunlu tutmayı seçebilir.
İstisnalar:
Şu durumlarda tür takma adı yerine newtype kullanın:
Date türü, iç hesaplamalarında Day, Month ve Year sayılarını ayırmak
için newtype'lar kullanabilir.Tuple indeksleri (.0, .1, ...) hızla kafa karıştırıcı hâle geldiği için
tuple'lar yerine adlandırılmış alanlara sahip türleri tercih edin.
İstisnalar:
Genel bir kural olarak, düşük seviyeli kütüphane kodu kullanıcıların ihtiyaçlarını proaktif olarak öngörmelidir; uygulama kodu ise trait impl'lerini gerektiğinde eklemelidir.
Bir metot, bir standart kütüphane trait'inin amacına ve davranışına uyuyorsa o
trait'i uygulayın. Örneğin Foo üzerinde fn from_str(&str) -> Self yazmak
yerine impl FromStr for Foo yapmalısınız.
Keşfedilebilirlik açısından hem metoda hem de trait impl'ine sahip olmak kabul edilebilir.
Bağlam için Clippy lint'ine bakın:
should_implement_trait
(trait listesi).
#[derive()] kullanın#[derive()] ile ifade edilebiliyorsa elle yazılan uygulamalardan kaçınılmalıdır.
dyn TraitKütüphanelerde fonksiyon parametrelerinde dyn Trait yerine generic'leri
(T: Trait veya impl Trait) kabul etmeyi tercih edin.
İstisnalar:
Gerekçe
Verimlilik
dyn Trait oluşturmak yağlı bir işaretçinin
(fat pointer) kurulmasını gerektirir ve trait üzerindeki bir fonksiyonun
her çağrısında bir vtable araması yapılır.dyn Trait alırsa derleyici, fonksiyon generic olsaydı
yapabileceği bazı iyileştirmeleri yapamaz; örneğin trait fonksiyonlarına
yapılan çağrıları satır içine alamaz.Bazı trait'ler
dyn uyumlu
değildir. Örneğin bir trait; generic fonksiyonları varsa, self
parametresi olmayan ilişkili fonksiyonları varsa veya Self döndüren
fonksiyonları varsa dyn uyumlu değildir.
Bir trait'i yalnızca şu amaçlarla tanımlayın:
Generic programlamayı etkinleştirmek için (generic fonksiyonların ve
türlerin kısıtlarında T: Trait kullanmak).
Bir impl Trait veya dyn Trait kabul etmek ya da döndürmek için.
Bir extension trait tanımlamak için (ancak extension trait'ler bölümündeki uyarılara da bakın).
Bir tür için işlevsellik uygulamak istediğiniz diğer tüm durumlarda bir inherent implementasyon kullanın.
Trait'leri yalnızca kodu düzenlemek için kullanmayın.
Extension trait, farklı bir crate'ten gelen bir türe metotlar ekleyen bir trait'tir.
Bir extension trait'i diğer trait'lerden ayıran şey, tek amacının türe metotlar
eklemek olmasıdır; generic'lerde (T: Trait, impl Trait) veya dinamik
gönderimde (dyn Trait) kullanılmak üzere tasarlanmamıştır. Bunu belirtmek
için extension trait'in adı ~Ext son ekiyle bitmelidir (örneğin
ForeignTypeExt).
Genel olarak, özgün türün sahibi olan ekip extension trait'in de sahibi olmalıdır.
İstisnalar:
Not: Bu kural özellikle extension trait'lerle ilgilidir. Bir trait'i girdi
trait'i olarak tanımlamak (ör. DateLike) ve onu bir dizi yabancı tür üzerinde
uygulamak oldukça yaygındır.
Örnekler
extern crate foreign_crate;
use foreign_crate::ForeignType;
trait ForeignTypeExt {
fn additional_method(&self);
}
impl ForeignTypeExt for ForeignType {
fn additional_method(&self) { ... }
}
Ayrıca yabancı bir trait'i uygulayan her tür için bir extension trait tanımlamak da mümkündür. Etkili olarak bu, yabancı trait'e varsayılan metotlar eklemeye eşdeğerdir:
extern crate foreign_crate;
use foreign_crate::ForeignTrait;
trait ForeignTraitExt {
fn additional_method(&self);
}
impl<T: ForeignTrait> ForeignTraitExt for T {
fn additional_method(&self) { ... }
}
Gerekçe
Extension trait ile genişletilen türün aynı ekibe ait olması, temel tür ile extension trait arasında veya iki extension trait arasında metot adı çakışmalarını önlemeye yardımcı olur.
Herkese açık alanlara sahip türler: Tüm alt alanlar tarafından uygulanan trait'leri uygulamayı düşünün.
Hata türleri: Mümkün olan her durumda Copy trait'ini uygulayın.
Copy türleri, sahipliği taşımadan değer olarak geçirilebildikleri için
yönetilmesi ve yayılması daha kolaydır. Bunun mümkün olmadığı bir durum,
hatanın String gibi Copy olmayan alanlar içermesi gerektiğidir.
Copy: Türün gelecekte de Copy olmaya devam edeceğinin makul ölçüde
açık olduğu her durumda uygulayın.
Clone: Bir dış kaynağı temsil etmedikleri sürece çoğu tür Clone
uygulamalıdır.
Default: Tür new() uyguluyorsa veya başka bir şekilde varsayılan bir
duruma sahipse uygulayın. Bağlam için Clippy lint'ine bakın:
new_without_default.
PartialEq / Eq: Kullanıcıların == operatörünü kullanmasını
istiyorsanız Eq yerine PartialEq uygulamayı tercih edin. Eq
uyguladığınızda ek olarak gereken yansımalılığın (reflexivity) farkında
olun (ayrıntılar: https://doc.rust-lang.org/std/cmp/trait.Eq.html).
PartialOrd / Ord: Kullanıcıların < veya > operatörlerini
kullanmasını istiyorsanız Ord yerine PartialOrd uygulamayı tercih edin.
Ord uyguladığınızda tam sıralama gerektirdiğinin farkında olun
(ayrıntılar: https://doc.rust-lang.org/std/cmp/trait.Ord.html).
Map anahtarları
Hash ve Eq uygulayın.Ord (ve ihtiyaç
duyduğu tüm trait'leri) uygulayın.From impl'lerini açık kurucu metotlarla eşleştirinBir türün, onu oluşturmanın birincil yollarından biri olması beklenen önemli bir
From impl'i varsa, ona bir inherent ::new(), ::new_from_foo() veya
::from_foo() metodu ekleyin.
Default uygulamalıdırTümü herkese açık alanlardan oluşan ve öncelikli olarak bir fonksiyon için
"seçenekler torbası" olarak kullanılan struct'lar Default uygulamalıdır.
Gerekçe
Bu, struct güncelleme sözdiziminin
(Struct { a: 0, ..Default::default()}) kolayca kullanılmasını sağlar.
Kullanım durumu ne kadar cazip görünürse görünsün, zehirlenmiş (poisoned) bir mutex'in içine erişmeye asla kalkışmayın.
async / .await kullanmayın [chrome]Chromium'da async / .await kullanmayın.
Chromium'da thread'ler yerine sequence'ları kullanmayı tercih edin.
fuchsia_sync::Mutex kullanın [Fuchsia]std::sync veya parking_lot içindekiler yerine Fuchsia'nın mutex türünü
kullanın.
Bir API'yi nasıl ifade edeceğinizi değerlendirirken şu sırayı tercih edin:
Sıradan Rust fonksiyonları, türleri, trait'leri, nesneleri ve modülleri. (Mümkün olduğunca makrolardan, kod üretiminden ve metaprogramlamadan kaçının).
macro_rules! tarzı makrolar.
Diğer kod üretim stratejileri (ör. genrule)
proc_macro makroları.
Bir makro veya kod üretimi kullanmaya karar verdiğinizde lütfen faydaları maliyetlere karşı tartın:
Gerekçe
Makro dışı olanakları tercih edin: Makrolar, mühendislerin kod tabanı genelinde çalışmasını zorlaştıran dil lehçeleri oluşturabilir. Bir okuyucu bir makro kullanımı gördüğünde, o kod hakkında makro kullanılmasaydı yapabileceğinden daha az varsayımda bulunabilir. Makroları doğru yazmak sıradan Rust kodundan daha zordur.
Basit kod üretimi için macro_rules! kullanın: Rust makroları AST üzerinde
çalıştığı için metinsel kod üretiminden daha az kırılgan olabilir.
Metotları adlandırırken Rust standart kütüphanesinin stilini izleyin. Ayrıca Comprehensive Rust kursundaki adlandırma kuralları yönergelerine de bakın.
Özel hususlar:
self kuralıyla birlikte as_...(), to_...() veya into_...()
gibi ön ekleri kullanın. Rust API Guidelines'ın
bu bölümüne
ve Clippy lint'ine
wrong_self_convention
de bakın.Lifetime'lar, özellikle tür lifetime'ları, açıklayıcı adlara sahip olmalıdır.
İstisnalar:
Cow gibi son derece generic soyutlamalar olan tür lifetime'ları.'_) kullanmak sorun değildir.for<'a>) için gerekiyorlarsa uygundur. Bu tür en fazla iki
yerel lifetime sorun değildir; daha fazlası gerekiyorsa açıklayıcı adlar
kullanmanızı şiddetle tavsiye ederiz.Generic parametreleriniz için temsil ettikleri şeyin türüyle ilgili kısa bir ad (tek veya çok karakterli) kullanın. Tek harfli generic'ler sorun değildir; ancak seçilen harf nesne türü hakkında bir fikir vermeli, ideal olarak en önemli trait sınırından alınmalıdır.
Generic'ler görece sınırsız bir bağlamdayken, yani Vec<T> gibi çoğu türle
kullanılmak üzere tasarlanmış bir API olduğunda T veya U sorun değildir.
Bir extension trait adlandırırken ada ~Ext son ekini
verin (örneğin ForeignTypeExt).
Başlatma fonksiyonlarını adlandırırken şu yönergeleri izleyin:
Kurucular: new() veya birden fazla varsa new_with_foo() kullanın.
Kurucu bir Result döndürüyorsa try_new() kullanın.
Değerden değere dönüşümler: İlişkili bir From impl'i ile birlikte
from_foo() kullanın. Bir işlemin ne zaman değerden değere dönüşüm olarak
kabul edilebileceğine dair ayrıntılar için
buraya
bakın.
Projenizin dışındaki birinin bilmesinin olası olmadığı kısaltmaları (özellikle akronimleri ve baş harf kısaltmalarını) kullanmayın. Bir sözcüğün içindeki harfleri silerek kısaltmayın.
Ayrıca Google C++ Stil Rehberi'ne bakın.
İstisnalar:
IpAddr. Bu değerlendirmeyi siz ve inceleyiciniz
yapmalıdır. Genel olarak tercih yine de kısaltmamaktan yana olmalıdır.Herkese açık API'lere dokümantasyon yorumları ekleyin; bunda missing_docs
lint'i yardımcı olabilir.
Dokümantasyon yorumları için en iyi uygulamalar:
Örnek eklemeyi düşünün: Her fonksiyonun örneği olması gerekmez, ancak crate'in nasıl çalıştığına dair bir fikir vermek için bir yerde yeterince işlevsellik örneklerle kapsanmalıdır.
Bozuk girdi nedeniyle panikleyebilecek fonksiyonlar için panik durumlarını her zaman belgeleyin.
Unsafe fonksiyonlar ve trait'ler için değişmezleri açıkça belgeleyin.
Büyük harf ve noktalama işareti kullanın.
Programlama dilinin temellerini açıklamayın. Örneğin şunu yapmayın:
```rust struct MockFileSystem { ... }
/// Birim testlerinde kullanılmak üzere FileSystem trait'inin sahte bir uygulaması. impl FileSystem for MockFileSystem ```
İstisnalar:
Yorumlar, kodun zaten söylemediği bir şeyi söylemelidir. Söyleyecek içgörülü
bir şey yoksa, fonksiyon adını önemsiz bir şekilde tekrarlamaktan iyidir ki
yorumu hiç yazmayın. Örneğin şunu yapmayın (bu durumda lint'i allow
etmek sorun değildir):
rust
/// Bir öğeyi kaldırır.
pub fn remove_element() -> Element {}
Özellikle adları kendilerini açıklıyorsa, tek tek enum varyantlarında dokümantasyonu atlamak genellikle sorun değildir.
Dokümantasyon yorumları nasıl biçimlendirilir
Dokümantasyon yorumları yazarken şu yönergeleri izleyin:
/// yorumlarını kullanın.//! yorumlarını
kullanın. Bu yorumlar dosyanın en üstüne yerleştirilmelidir.//) kullanın./// ve //!
yorumlarını kullanarak doğrudan belgelemeyi tercih edin. Bu, kaynak kodu
görüntülerken veya değiştirirken dokümantasyonun doğrudan okunabilir
olmasını sağlar.Doc yorumlarını (/// ve //!) sıradan yorumlarla (//) karıştırmak kabul
edilebilir, hatta teşvik edilir. Herkese açık olarak belgelemek istediğiniz bir
şey ve uygulama yorumu olarak not etmek istediğiniz başka bir şey varsa ikisini
birden kullanın! Örneğin:
/// Verilen `name` ile ilk `FooItem`'ı bulur, yoksa `None` döndürür.
// TODO: crbug.com/12345 - `name` String değil, &str olmalı.
pub fn get_named(items: &[FooItem], name: String) -> Option<&FooItem> {...}
Gerekçe
Pek çok dilde (hepsinde değil), doc yorumları ile uygulama yorumları için
farklı yorumlama gelenekleri vardır (örneğin fazladan * karakterleri). Rust
ve Python yalnızca farklı yorumlama geleneklerine sahip olmakla kalmaz, tamamen
farklı bir sözdizimine de sahiptir. Bunun Rust ekosistemi için somut faydaları
vardır:
rustdoc gibi dokümantasyon üreticileri, IDE özellikleri ve
Kythe dâhil olmak üzere araçlar tarafından tüketilebilir.macro_rules!
makroları hem de prosedürel makrolar dâhil olmak üzere makrolar tarafından
üretilebilir ve tüketilebilir.Aynı işlevsellik güvenli kodla uygulanamıyorsa unsafe kod yazmayın.
Unsafe kod yazmanız gerekiyorsa:
SAFETY: ile başlayan bir güvenlik
yorumu ekleyin.Kararsız (unstable) özellikleri kullanmayın.
İstisnalar:
Getter ve setter'ların yazımı ve adlandırılması şu yönergeleri izlemelidir:
&T veya T: Copy ise değer olarak T döndüren getter için foo().&mut T döndüren getter için foo_mut().None veya başka bir varsayılan duruma ayarlayarak)
ve eski değeri döndüren getter için take_foo().T alan setter için set_foo().with_foo() /
with_mut_foo() (ayrıntılar için aşağıya bakın). Böyle
bir erişimci object.with_foo(|foo| frob(foo)) şeklinde çağrılır.replace_foo().Örnekler
impl Stuff {
fn foo(&self) -> &Foo { ... }
fn foo_mut(&mut self) -> &mut Foo { ... }
/// "baz" özelliğini `None` olarak ayarlar ve eski değeri döndürür.
fn take_baz(&mut self) -> Option<Baz> { ... }
fn set_foo(&mut self, foo: Foo) { ... }
/// `f`'i "bar" özelliğinin bir ödünç alımıyla çağırır.
/// `f`'in dönüş değerini döndürür.
fn with_bar<R>(&self, f: impl FnOnce(&Bar) -> R) -> R { ... }
/// `f`'i "bar" özelliğinin değiştirilebilir bir ödünç alımıyla çağırır.
/// `f`'in dönüş değerini döndürür.
fn with_mut_bar<R>(&self, f: impl FnOnce(&mut Bar) -> R) -> R { ... }
/// "foo" özelliğini `foo` olarak ayarlar ve eski değeri döndürür.
fn replace_foo(&mut self, foo: Foo) -> Foo { ... }
}
Closure kabul eden erişimciler (.with_foo()) tipik olarak
object.with_foo(|foo| do_something_with(foo)) şeklinde çağrılır. Şu
durumlarda iyi bir seçim olabilirler:
Özellik bir iç değiştirilebilirlik sarmalayıcısı içinde tutulduğunda (ör.
Mutex veya RefCell). Bu mekanizmalar özelliğe erişimi bir kapsamla
sınırlamak zorundadır ve closure bu kapsamı sağlar. Bu genellikle kapsamı
uygulayan RAII nesnesini (ör. MutexGuard<T>) doğrudan döndürmekten daha
iyidir; çünkü ikincisi bir uygulama ayrıntısını sızdırırdı.
Bir özellik değiştirildikten sonra takip işi veya doğrulama gerektiğinde ve
set_foo() erişimcisi kullanılamadığında (örneğin özelliğin türünü
klonlamak pahalıysa veya hiç klonlanamıyorsa).
Bir with_foo() erişimcisi genellikle closure'ın bir değer döndürmesine ve bunu
with_foo()'dan döndürerek iletmesine izin verir.
Biri salt okunur, diğeri değiştirilebilir erişim için iki closure kabul eden
erişimci sağlamanız gerekiyorsa bunları with_foo() ve with_mut_foo() olarak
adlandırmak yaygındır. Aksi hâlde with_foo() değiştiren erişimciler için de
sıkça kullanılır.
len() uyguluyorsanız is_empty() de uygulayın. Bağlam için Clippy lint'ine
bakın:
len_without_is_empty.
#[doc(hidden)] olarak işaretlenmiş öğelere erişmeyin#[doc(hidden)] olarak işaretlenmiş öğeleri özel (private) kabul edin. Kodu
sizin değilse bu öğelere erişmeyin.
const fonksiyonları sorumlu bir şekilde kullanınHerkese açık bir fonksiyonu yalnızca gelecekte de const olarak kalmasını
bekliyorsanız (veya değiştiğinde istemcilerin geçişine yardımcı olmayı taahhüt
edebiliyorsanız) const olarak işaretleyin.
Gerekçe
Bir const fonksiyonu normal bir fonksiyona dönüştürmek geriye dönük uyumsuz
(breaking) bir değişikliktir.
Operatör aşırı yüklemelerini yalnızca kodu daha kısa hâle getirmek için ("sözdizimsel şeker" olarak) değil, özgün matematiksel/mantıksal amaçları için kullanın.
#[non_exhaustive] uygulayınBir enum, diğer ekipler tarafından kullanılan herkese açık bir API'nin parçasıysa
onu #[non_exhaustive] olarak işaretleyin.
İstisnalar:
enum Color { Red, Green, Blue }).enum Visibility { Visible, Hidden })Bağlam için Clippy lint'ine bakın:
exhaustive_enums.
Gerekçe
Bir enum'u #[non_exhaustive] olarak işaretlemek, kullanıcıları match
ifadelerinde bir joker kol (_ => ...) eklemeye zorlar. Bu, enum'a daha sonra
yeni varyantlar eklendiğinde kodun derlenmeye devam etmesini sağlar.
Bu kural öncelikli olarak ekip sınırları arasında kullanılan enum'lar için önemlidir; çünkü kırılmanın kapsamı büyük olabilir. Yalnızca az sayıda yerde kullanılan enum'lar büyük bir kırılma yaratamaz ve bu nedenle bu kuralı yok sayabilir.
#[non_exhaustive] uygulayınDiğer ekipler tarafından kullanılan herkese açık bir API'nin parçası olan,
herkese açık alanlara sahip struct'lar #[non_exhaustive] olarak
işaretlenmelidir.
İstisnalar:
Person { name: "foo", ..Default::default() })
kullanmasını etkinleştirmek istiyorsanız.Gerekçe
Tüm alanları herkese açık olan bir struct'a yeni bir alan eklemek (özel bir alan bile olsa) geriye dönük uyumsuz bir değişikliktir; çünkü çağıranlar artık bir struct literalinin tüm alanlarını adlandıramaz.
#[non_exhaustive] bir struct, aynı crate içindeki kod dışında struct literal
sözdizimiyle oluşturulamaz. Bu, kullanıcıları bozmadan yeni özel alanlar
eklenmesine olanak tanır.
Bu kural öncelikli olarak ekip sınırları arasında kullanılan struct'lar için önemlidir; çünkü kırılmanın kapsamı büyük olabilir. Yalnızca az sayıda yerde kullanılan struct'lar büyük bir kırılma yaratamaz ve bu nedenle bu kuralı yok sayabilir.
#[must_use]Şu durumlarda fonksiyonlarda #[must_use] kullanın:
Çağıran, dönüş değerini yok sayarsa fonksiyonu doğru kullanamıyorsa. Buna
HashMap::get() gibi saf fonksiyonlar (yan etkisi olmayan fonksiyonlar) da
dâhildir.
Dönüş değeri bir hatayı veya fonksiyonun davranışına dair başka bir kritik
bilgiyi temsil eden herhangi bir fonksiyon. Dönüş türü zaten #[must_use]
olarak işaretlenmişse (örneğin Result için durum böyledir), fonksiyonun
#[must_use] olarak işaretlenmesi gerekmez.
#[must_use]Yukarıdaki kurallara göre aksi hâlde #[must_use]
olarak işaretlenecek fonksiyonlar tarafından döndürülmek üzere tasarlanmış
türlerde #[must_use] kullanın.
Örnekler:
Tür bir hata türüdür (Result veya StatusOr benzeri).
Tür, tembel değerlendirilen bir hesaplamayı temsil eder. Başka bir deyişle, fonksiyon çağrısı tek başına bir şey yapmaz ve istenen yan etkiyi oluşturmak için dönüş değerine erişilmesi gerekir.
main() öncesinde kod çalıştırmayınÖrneğin ctor crate gibi teknikler
kullanarak main() öncesinde kod çalışmasına neden olmayın.
Kod tabanına aşina olan birinin çıkarılan türlerin ne olduğunu anlayabilmesi için yeterince açık tür ekleyin.
Türleri
turbofish
olarak eklemek yerine değişken bildirimlerine eklemeyi tercih edin. Derleyici
sizi bir turbofish eklemeye zorluyorsa genellikle bir _ çıkarım işaretçisi
eklemek sorun değildir.
Muhtemelen tür eklemeniz gereken özel durumlar:
.into() veya .parse() gibi generic dönüş türüne sahip metotları
çağırırken.Girdi verisi panikleyemeyeceklerini garanti ediyorsa panikleyebilecek
işlemlerden kaçının. Örneğin geçerli olduğu bilinen bir değer üzerinde
Option::unwrap kullanmak yerine, doğruluğu derleyiciye kanıtlamak için desen
eşleştirmeyi kullanın.
// İyi: match kolu, `x`'in `Some` olduğunu derleyiciye kanıtlar.
// Bir panik açıkça mümkün değildir.
if let Some(value) = option {
use_value(value);
}
// Kötü: `unwrap`'ın doğruluğu, `option`'ın `Some` olduğuna dair çalışma
// zamanı kontrolüne dayanır.
if option.is_some() {
use_value(option.unwrap());
}
unwrap yerine expect tercih edinBir değişmez tür sistemi içinde ifade edilemediği için panik gerekiyorsa,
nedeni belgelemek için unwrap yerine expect tercih edin.
non_exhaustive enum'larda desenleri atlamayınnon_exhaustive enum'lar üzerinde match ifadelerinde tüm enum varyantlarını
listelemeyi tercih edin.
Belirli varyantlarla ilgilenmiyorsanız onları joker kola koyun. Yalnızca belirli
varyantlarla ilgileniyorsanız bir if let veya let … else {} kullanın.
Örnekler:
#[non_exhaustive]
pub enum Shape {
Circle,
Square,
Triangle,
}
fn process_shape(shape: Shape) {
match shape {
Shape::Circle => println!("It's a circle"),
Shape::Square => println!("It's a square"),
// Triangle ve gelecekteki varyantlar herhangi bir işlem gerektirmez.
Shape::Triangle | _ => {}
}
}
fn process_shape(shape: Shape) {
if let Shape::Circle = shape {
println!("It's a circle");
}
}
fn process_shape(shape: Shape) {
match shape {
Shape::Circle => println!("It's a circle"),
_ => {}
}
}
İstisnalar:
match, if let'ten
daha temizse.Bazı doğrulamalar kendilerini açıklasa da çoğu, bir doğrulamanın neden
gerçekleşmesinin beklendiğini veya ihlaline neyin yol açtığını netleştiren ek
bağlamdan fayda görür. Bu bağlamı iletmek için assert!'in isteğe bağlı format
dizesi argümanını kullanın.
Bağlam eklemeyi her zaman düşünmelisiniz; özellikle şu durumlarda:
Örnekler
Bir doğrulamanın neden gerçekleşmesi gerektiğini açıklamak
assert!(
values.is_sorted(),
"`values` is populated from a `PriorityQueue`, so we expect it to be sorted"
);
Doğrulamanın neden başarısız olduğunu netleştirmek için bir değeri günlüğe kaydetmek
assert!(refund > 0, "Refund must be positive, got {refund:?}");
Bir API'nin yanlış kullanımına işaret etmek
impl Database {
...
fn execute_query(&self, query: &str) {
assert!(self.stream.is_some(), "Call connect() before executing queries");
}
...
}
Bağlam dizesini yalnızca koşulu tekrarlamak için kullanmayın
assert!(values.is_sorted(), "values should always be sorted");
Kombinasyon zincirlerini okunabilirliği bozacak şekilde kullanmaktan kaçının. Özellikle:
for döngüsünün başlığında kombinasyon zinciri oluşturmayın. Bunun
yerine kombinasyon zincirini bir değişkene atayın, ardından o değişkeni
for döngüsünde tüketin.Iterator::for_each kullanmayın.
Bir for döngüsü genellikle okunması daha kolaydır.Kombinasyonlar, daha kısa zincirlerde ve kombinasyonun kendisinin, karşılık
gelen for döngüsünde yazılması gerekecek çok miktarda mantık sağladığı
durumlarda tercih edilir.
Örnekler
Karmaşık kombinasyon zincirleri
Koşullar içerdiği ve dış durumu değiştirdiği için okunması zor olan bir kombinasyon zinciri örneği:
let mut error_count = 0;
let _processed_items: Vec<_> = items
.into_iter()
.map(transformation_that_may_fail)
.map(|result| {
if result.is_err() {
error_count += 1;
}
result
})
.filter_map(Result::ok)
.collect();
Karşılık gelen for döngüsü daha kolay okunur:
let mut error_count = 0;
let mut processed_items = Vec::with_capacity(items.len());
for item in items {
match transformation_that_may_fail(item) {
Ok(val) => processed_items.push(val),
Err(_) => error_count += 1,
}
}
For döngülerinde kombinasyon zincirleri
Bir for döngüsünün başlığında kombinasyon zinciri oluşturmaktan kaçının:
for bug in bug_descriptions
.iter()
.filter_map(|description| parse_bug_description(description).ok())
.filter(|bug| bug.priority <= Priority::P1)
{
assign_bug(bug);
}
Bunun yerine kombinasyon zincirini bir değişkene atayın. Bu dağınıklığı azaltır ve değişken adı kombinasyon zincirinin ne yaptığını belgeler:
let urgent_bugs = bug_descriptions
.iter()
.filter_map(|description| parse_bug_description(description).ok())
.filter(|bug| bug.priority <= Priority::P1);
for bug in urgent_bugs {
assign_bug(bug);
}
Kombinasyon zinciri ne zaman tercih edilmeli
Kombinasyon zincirinin tercih edildiği bir örnek:
let threshold_exceeded = values.iter().any(|&v| v > threshold);
Karşılık gelen for döngüsü daha zor okunur ve any() içindeki mantığı yeniden
icat eder:
let mut threshold_exceeded = false;
for v in values {
if v > threshold {
threshold_exceeded = true;
break;
}
}
panic::catch_unwind çağırmayınBirinci taraf Rust kodunda panic::catch_unwind kullanımı yasaktır.
İstisnalar:
catch_unwind kurtarma mekanizmasıyla
sarmalamayı seçebilir. Kod yazarları, kod tabanının paniklerden kaçınmak
için elinden gelenin çoğunu yaptığına inceleyiciyi ikna etmelidir. Bu
istisna yalnızca panic=unwind ile çalıştırılma olasılığı olan kod için
geçerlidir (dolayısıyla
Chrome, Android,
Fuchsia için geçerli değildir).Gerekçe
Rust kodu birçok ortamda kullanılabilir ve panic::catch_unwind yalnızca
panic=unwind olan ortamlarda doğru çalışır. Üretim ikili dosyalarının nihai
panik davranışı henüz belirlenmemiş olsa da Chrome, Android ve Fuchsia'nın üçü
de panic=abort gerektirir.
format! / write! tercih edinŞu durumlarda diğer alternatifler yerine format!() ve write!() tercih edin:
a + b + c yerine
format!("{a}{b}{c}") kullanın.str::replace
kullanmak yerine).Gerekçe
format! / write!, + ile string birleştirmeye göre daha az tahsis
gerektirir.Çoğu kod tabanı #[test] yerine kendi özel test çerçevesini kullanır.
Chrome rust_gtest_interop kullanın. Bu, Rust testlerini C++ test ikili dosyalarına entegre etmeyi mümkün kılar.
Fuchsia #[fuchsia::test] kullanın. Ayrıntılar
için
buraya
bakın.
Bu belge, Google Rust Style Guide metninin Türkçe çevirisidir. Özgün metin CC BY 4.0 lisansıyla yayımlanmaktadır; çeviri de aynı lisans altında sunulmaktadır.