Traitler ile Paylaşılan Davranışı Tanımlamak
Bir trait, belirli bir türün sahip olduğu ve diğer türlerle paylaşabileceği işlevselliği tanımlar. Paylaşılan davranışı soyut bir şekilde tanımlamak için traitleri kullanabiliriz. Jenerik bir türün belirli bir davranışa sahip herhangi bir tür olabileceğini belirtmek için trait sınırlarını kullanabiliriz.
Not: Traitler, bazı farklılıkları olsa da, diğer dillerde genellikle arayüzler olarak adlandırılan bir özelliğe benzerdir.
Bir Trait Tanımlamak
Bir türün davranışı, o tür üzerinde çağırabileceğimiz metotlardan oluşur. Tüm bu türlerde aynı metotları çağırabiliyorsak farklı türler aynı davranışı paylaşır. Trait tanımları, bazı amaçları gerçekleştirmek için gerekli bir dizi davranışı tanımlamak amacıyla metot imzalarını gruplamanın bir yoludur.
Örneğin, çeşitli türlerde ve miktarlarda metin tutan birden fazla struct’ımız (yapımız) olduğunu varsayalım: Belirli bir konumda dosyalanmış bir haber hikayesini tutan HaberMakalesi struct’ı ve yeni bir gönderi mi, yeniden paylaşım mı yoksa başka bir gönderiye yanıt mı olduğunu belirten meta verilerle birlikte en fazla 280 karaktere sahip olabilen SosyalGonderi struct’ı.
Bir HaberMakalesi veya SosyalGonderi örneğinde depolanabilecek verilerin özetlerini görüntüleyebilen aggregator (toplayıcı) adında bir medya toplayıcı kütüphane crate’i yapmak istiyoruz. Bunu yapmak için, her bir türden bir özete ihtiyacımız var ve bir örnek üzerinde ozetle metodu çağırarak bu özeti isteyeceğiz. Liste 10-12 bu davranışı ifade eden açık bir Ozet trait’inin tanımını gösterir.
pub trait Ozet {
fn ozetle(&self) -> String;
}
ozetle metodunun sağladığı davranıştan oluşan bir Ozet trait’iBurada, trait anahtar kelimesini ve ardından bu durumda Ozet olan trait adını kullanarak bir trait bildiriyoruz. Ayrıca, birkaç örnekte göreceğimiz gibi, bu crate’e bağlı olan cratelerin de bu trait’ten yararlanabilmesi için trait’i pub olarak bildiriyoruz. Süslü parantezlerin (curly brackets) içinde, bu trait’i uygulayan türlerin davranışlarını tanımlayan metot imzalarını bildiririz, bu durumda fn ozetle(&self) -> String’dir.
Metot imzasından sonra süslü parantezler içinde bir uygulama sağlamak yerine noktalı virgül kullanırız. Bu trait’i uygulayan her tür, metodun gövdesi için kendi özel davranışını sağlamalıdır. Derleyici, Ozet trait’ine sahip olan her türün tam olarak bu imzayla tanımlanmış ozetle metoduna sahip olmasını zorunlu kılacaktır.
Bir trait gövdesinde birden fazla metoda sahip olabilir: Metot imzaları her satıra bir tane olacak şekilde listelenir ve her satır noktalı virgülle biter.
Bir Tür Üzerinde Trait Uygulamak (Implementing)
Ozet trait’inin metotlarının istenen imzalarını tanımladığımıza göre, artık onu medya toplayıcımızdaki türler üzerinde uygulayabiliriz. Liste 10-13, ozetle metodunun dönüş değerini oluşturmak için manşeti, yazarı ve konumu kullanan HaberMakalesi struct’ı üzerinde Ozet trait’inin bir uygulamasını gösterir. SosyalGonderi struct’ı için, gönderi içeriğinin halihazırda 280 karakterle sınırlı olduğunu varsayarak ozetle metodunu kullanıcı adı ve ardından gönderinin tüm metni olarak tanımlıyoruz.
pub trait Ozet {
fn ozetle(&self) -> String;
}
pub struct HaberMakalesi {
pub manset: String,
pub konum: String,
pub yazar: String,
pub icerik: String,
}
impl Ozet for HaberMakalesi {
fn ozetle(&self) -> String {
format!("{}, {} ({})", self.manset, self.yazar, self.konum)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn ozetle(&self) -> String {
format!("{}: {}", self.kullanici_adi, self.icerik)
}
}
HaberMakalesi ve SosyalGonderi türleri üzerinde Ozet trait’inin uygulanmasıBir tür üzerinde bir trait uygulamak, normal metotları uygulamaya benzer. Aradaki fark, impl kelimesinden sonra uygulamak istediğimiz trait adını koymamız, ardından for anahtar kelimesini kullanmamız ve sonra trait’i uygulamak istediğimiz türün adını belirtmemizdir. impl bloğunun içine, trait tanımında belirtilen metot imzalarını koyarız. Her imzanın sonuna noktalı virgül eklemek yerine süslü parantezler kullanırız ve trait’in metotlarının o belirli tür için sahip olmasını istediğimiz belirli davranışla metot gövdesini doldururuz.
Kütüphane HaberMakalesi ve SosyalGonderi üzerinde Ozet trait’ini uyguladığına göre, crate kullanıcıları normal metotları çağırdığımız şekilde HaberMakalesi ve SosyalGonderi örneklerinde trait metotlarını çağırabilir. Tek fark, kullanıcının türlerin yanı sıra trait’i de kapsama dahil etmesi gerektiğidir. İşte bir ikili crate’in aggregator kütüphane crate’imizi nasıl kullanabileceğine dair bir örnek:
use aggregator::{Ozet, SosyalGonderi};
fn main() {
let gonderi = SosyalGonderi {
kullanici_adi: String::from("horse_ebooks"),
icerik: String::from(
"elbette, muhtemelen zaten bildiğiniz gibi, insanlar",
),
yanit: false,
yeniden_paylasim: false,
};
println!("1 yeni gönderi: {}", gonderi.ozetle());
}
Bu kod 1 yeni gönderi: horse_ebooks: elbette, muhtemelen zaten bildiğiniz gibi, insanlar yazdırır.
aggregator crate’ine bağlı olan diğer crateler de Ozet trait’ini kendi türleri üzerinde uygulamak için Ozet trait’ini kapsama dahil edebilirler. Dikkat edilmesi gereken bir kısıtlama, bir tür üzerinde bir trait’i ancak trait veya türden biri ya da her ikisi bizim crate’imiz için yerel ise uygulayabilmemizdir. Örneğin, SosyalGonderi türü bizim aggregator crate’imiz için yerel olduğundan, aggregator crate işlevselliğimizin bir parçası olarak SosyalGonderi gibi özel bir tür üzerinde Display (Göster) gibi standart kütüphane traitlerini uygulayabiliriz. Ayrıca, Ozet trait’i aggregator crate’imiz için yerel olduğundan, aggregator crate’imizde Vec<T> üzerinde Ozet uygulayabiliriz.
Ancak harici traitleri harici türler üzerinde uygulayamayız. Örneğin, aggregator crate’imiz içerisinde Vec<T> üzerinde Display trait’ini uygulayamayız, çünkü hem Display hem de Vec<T> standart kütüphanede tanımlanmıştır ve aggregator crate’imiz için yerel değildir. Bu kısıtlama, tutarlılık adı verilen bir özelliğin ve daha spesifik olarak ebeveyn türün mevcut olmaması nedeniyle bu adı alan yetim kuralının bir parçasıdır. Bu kural, başkalarının kodunun sizin kodunuzu, sizin kodunuzun da başkalarının kodunu bozamamasını sağlar. Kural olmasaydı, iki crate aynı tür için aynı trait’i uygulayabilirdi ve Rust hangi uygulamayı kullanacağını bilemezdi.
Varsayılan Uygulamaları (Default Implementations) Kullanmak
Bazen her türdeki tüm metotlar için uygulama gerektirmek yerine bir trait’teki metotların bazılarında veya tümünde varsayılan bir davranışa sahip olmak yararlıdır. Böylece, trait’i belirli bir tür üzerinde uygularken, her metodun varsayılan davranışını koruyabilir veya geçersiz kılabiliriz.
Liste 10-14’te, Liste 10-12’de yaptığımız gibi yalnızca metot imzasını tanımlamak yerine Ozet trait’inin ozetle metodu için varsayılan bir string (dizgi) belirliyoruz.
pub trait Ozet {
fn ozetle(&self) -> String {
String::from("(Devamını oku...)")
}
}
pub struct HaberMakalesi {
pub manset: String,
pub konum: String,
pub yazar: String,
pub icerik: String,
}
impl Ozet for HaberMakalesi {}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn ozetle(&self) -> String {
format!("{}: {}", self.kullanici_adi, self.icerik)
}
}
ozetle metodunun varsayılan bir uygulamasıyla birlikte Ozet trait’ini tanımlamakHaberMakalesi örneklerini özetlerken varsayılan bir uygulama kullanmak için impl Ozet for HaberMakalesi {} şeklinde boş bir impl bloğu belirtiriz.
Artık doğrudan HaberMakalesi üzerinde ozetle metodunu tanımlamıyor olsak da, varsayılan bir uygulama sağladık ve HaberMakalesi’nin Ozet trait’ini uyguladığını belirttik. Sonuç olarak, HaberMakalesi örneğinde ozetle metodunu hala şu şekilde çağırabiliriz:
use aggregator::{self, HaberMakalesi, Ozet};
fn main() {
let makale = HaberMakalesi {
manset: String::from("Penguenler Stanley Cup Şampiyonluğunu kazandı!"),
konum: String::from("Pittsburgh, PA, ABD"),
yazar: String::from("Iceburgh"),
icerik: String::from(
"Pittsburgh Penguenleri bir kez daha NHL'deki en iyi \
hokey takımı.",
),
};
println!("Yeni makale mevcut! {}", makale.ozetle());
}
Bu kod Yeni makale mevcut! (Devamını oku...) yazdırır.
Varsayılan bir uygulama oluşturmak, Liste 10-13’teki SosyalGonderi üzerindeki Ozet uygulamasında herhangi bir şeyi değiştirmemizi gerektirmez. Bunun nedeni, varsayılan bir uygulamayı geçersiz kılma sözdiziminin, varsayılan bir uygulaması olmayan bir trait metodunu uygulama sözdizimiyle aynı olmasıdır.
Varsayılan uygulamalar, diğer metotların varsayılan bir uygulaması olmasa bile aynı trait’teki diğer metotları çağırabilir. Bu şekilde, bir trait çok fazla faydalı işlevsellik sağlayabilir ve uygulayıcılardan sadece küçük bir kısmını belirtmelerini isteyebilir. Örneğin, uygulamasının gerekli olduğu yazari_ozetle metodu olan bir Ozet trait’i tanımlayabiliriz ve ardından yazari_ozetle metodunu çağıran varsayılan bir uygulamaya sahip ozetle metodu tanımlayabiliriz:
pub trait Ozet {
fn yazari_ozetle(&self) -> String;
fn ozetle(&self) -> String {
format!(
"({} yazarından daha fazlasını okuyun...)",
self.yazari_ozetle()
)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn yazari_ozetle(&self) -> String {
format!("@{}", self.kullanici_adi)
}
}
Bu Ozet sürümünü kullanmak için, trait’i bir tür üzerinde uygularken sadece yazari_ozetle metodunu tanımlamamız gerekir:
pub trait Ozet {
fn yazari_ozetle(&self) -> String;
fn ozetle(&self) -> String {
format!(
"({} yazarından daha fazlasını okuyun...)",
self.yazari_ozetle()
)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn yazari_ozetle(&self) -> String {
format!("@{}", self.kullanici_adi)
}
}
yazari_ozetle’yi tanımladıktan sonra, SosyalGonderi struct’ının örnekleri üzerinde ozetle’yi çağırabiliriz ve ozetle’nin varsayılan uygulaması, sağladığımız yazari_ozetle tanımını çağıracaktır. yazari_ozetle uygulamasını gerçekleştirdiğimiz için Ozet trait’i bize daha fazla kod yazmamıza gerek kalmadan ozetle metodunun davranışını verdi. İşte şöyle görünür:
use aggregator::{self, SosyalGonderi, Ozet};
fn main() {
let gonderi = SosyalGonderi {
kullanici_adi: String::from("horse_ebooks"),
icerik: String::from(
"elbette, muhtemelen zaten bildiğiniz gibi, insanlar",
),
yanit: false,
yeniden_paylasim: false,
};
println!("1 yeni gönderi: {}", gonderi.ozetle());
}
Bu kod 1 yeni gönderi: (@horse_ebooks yazarından daha fazlasını okuyun...) yazdırır.
Aynı metodun geçersiz kılınan bir uygulamasından varsayılan uygulamayı çağırmanın mümkün olmadığını unutmayın.
Traitleri Parametre Olarak Kullanmak
Artık traitlerin nasıl tanımlanacağını ve uygulanacağını bildiğinize göre, birçok farklı türü kabul eden fonksiyonları tanımlamak için traitleri nasıl kullanacağınızı keşfedebiliriz. Ozet trait’ini uygulayan bir türde olan oge parametresi üzerinde ozetle metodunu çağıran bildir fonksiyonunu tanımlamak için Liste 10-13’te HaberMakalesi ve SosyalGonderi türleri üzerinde uyguladığımız Ozet trait’ini kullanacağız. Bunu yapmak için, impl Trait sözdizimini şu şekilde kullanırız:
pub trait Ozet {
fn ozetle(&self) -> String;
}
pub struct HaberMakalesi {
pub manset: String,
pub konum: String,
pub yazar: String,
pub icerik: String,
}
impl Ozet for HaberMakalesi {
fn ozetle(&self) -> String {
format!("{}, {} ({})", self.manset, self.yazar, self.konum)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn ozetle(&self) -> String {
format!("{}: {}", self.kullanici_adi, self.icerik)
}
}
pub fn bildir(oge: &impl Ozet) {
println!("Son dakika haberi! {}", oge.ozetle());
}
oge parametresi için somut bir tür (concrete type) yerine impl anahtar kelimesini ve trait adını belirtiyoruz. Bu parametre, belirtilen trait’i uygulayan herhangi bir türü kabul eder. bildir’in gövdesinde, oge üzerinde Ozet trait’inden gelen ozetle gibi metotları çağırabiliriz. bildir çağırabiliriz ve herhangi bir HaberMakalesi veya SosyalGonderi örneği geçebiliriz. Fonksiyonu String veya i32 gibi herhangi bir türle çağıran kod derlenmeyecektir çünkü bu türler Ozet trait’ini uygulamazlar.
Trait Sınırı (Trait Bound) Sözdizimi
impl Trait sözdizimi basit durumlar için işe yarar ancak aslında trait sınırı (trait bound) olarak bilinen daha uzun bir formun sözdizimsel şekeri gibidir; şu şekilde görünür:
pub fn bildir<T: Ozet>(oge: &T) {
println!("Son dakika haberi! {}", oge.ozetle());
}
Bu uzun form, önceki bölümdeki örneğe eşdeğerdir ancak daha uzundur. Jenerik tür parametresinin bildirimi ile birlikte iki nokta üst üsteden sonra ve açılı parantezlerin içine trait sınırlarını yerleştiririz.
impl Trait sözdizimi kullanışlıdır ve basit durumlarda kodu daha özlü hale getirirken, daha tam olan (fuller) trait sınırı sözdizimi diğer durumlarda daha fazla karmaşıklığı ifade edebilir. Örneğin, Ozet uygulayan iki parametremiz olabilir. Bunu impl Trait sözdizimi ile yapmak şuna benzer:
pub fn bildir(oge1: &impl Ozet, oge2: &impl Ozet) {
Eğer bu fonksiyonun oge1 ve oge2’nin farklı türlere sahip olmasına izin vermesini istiyorsak (her iki tür de Ozet uyguladığı sürece) impl Trait kullanmak uygundur. Ancak her iki parametreyi de aynı türe sahip olmaya zorlamak istersek, şu şekilde bir trait sınırı kullanmalıyız:
pub fn bildir<T: Ozet>(oge1: &T, oge2: &T) {
oge1 ve oge2 parametrelerinin türü olarak belirtilen jenerik tür T, fonksiyonu öyle bir kısıtlar ki, oge1 ve oge2 için argüman olarak iletilen değerin somut türü (concrete type) aynı olmalıdır.
+ Sözdizimi ile Birden Fazla Trait Sınırı
Birden fazla trait sınırı da belirtebiliriz. Diyelim ki bildir’in oge üzerinde ozetle ile birlikte ekran biçimlendirmesini de kullanmasını istiyoruz: bildir tanımında oge’nin hem Display hem de Ozet trait’lerini uygulaması gerektiğini belirtiyoruz. Bunu + sözdizimini kullanarak yapabiliriz:
pub fn bildir(oge: &(impl Ozet + Display)) {
+ sözdizimi jenerik türler üzerindeki trait sınırlarıyla da geçerlidir:
pub fn bildir<T: Ozet + Display>(oge: &T) {
Belirtilen iki trait sınırı ile bildir’in gövdesi ozetle’yi çağırabilir ve oge’yi formatlamak için {} kullanabilir.
where Cümlecikleriyle Daha Açık Trait Sınırları
Çok fazla trait sınırı kullanmanın dezavantajları vardır. Her jeneriğin kendi trait sınırları vardır, bu nedenle birden fazla jenerik tür parametresine sahip fonksiyonlar, fonksiyonun adı ve parametre listesi arasında çok fazla trait sınırı bilgisi içerebilir, bu da fonksiyon imzasının okunmasını zorlaştırır. Bu nedenle, Rust fonksiyon imzasından sonra bir where (nerede/şartıyla) cümleciği içinde trait sınırlarını belirtmek için alternatif bir sözdizimine sahiptir. Yani bunu yazmak yerine:
fn bazi_fonksiyonlar<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
Şu şekilde bir where cümleciği kullanabiliriz:
fn bazi_fonksiyonlar<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
unimplemented!()
}
Bu fonksiyonun imzası daha az karmaşıktır: Fonksiyon adı, parametre listesi ve dönüş türü birbirine yakındır, tıpkı çok fazla trait sınırı olmayan bir fonksiyona benzer şekilde.
Traitleri Uygulayan (Implement) Türler Döndürmek
impl Trait sözdizimini, burada gösterildiği gibi bir trait uygulayan bazı türlerden bir değer döndürmek için dönüş pozisyonunda da kullanabiliriz:
pub trait Ozet {
fn ozetle(&self) -> String;
}
pub struct HaberMakalesi {
pub manset: String,
pub konum: String,
pub yazar: String,
pub icerik: String,
}
impl Ozet for HaberMakalesi {
fn ozetle(&self) -> String {
format!("{}, {} ({})", self.manset, self.yazar, self.konum)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn ozetle(&self) -> String {
format!("{}: {}", self.kullanici_adi, self.icerik)
}
}
fn ozetlenebilir_dondur() -> impl Ozet {
SosyalGonderi {
kullanici_adi: String::from("horse_ebooks"),
icerik: String::from(
"elbette, muhtemelen zaten bildiğiniz gibi, insanlar",
),
yanit: false,
yeniden_paylasim: false,
}
}
Dönüş türü olarak impl Ozet kullanarak, ozetlenebilir_dondur fonksiyonunun somut türün adını vermeden Ozet trait’ini uygulayan bir tür döndürdüğünü belirtiyoruz. Bu durumda, ozetlenebilir_dondur bir SosyalGonderi döndürür ancak bu fonksiyonu çağıran kodun bunu bilmesi gerekmez.
Dönüş türünü sadece uyguladığı trait ile belirtebilme yeteneği, özellikle Bölüm 13’te ele aldığımız kapanışlar ve yineleyiciler bağlamında faydalıdır. Kapanışlar ve yineleyiciler, yalnızca derleyicinin bildiği türler veya belirtilmesi çok uzun olan türler yaratır. impl Trait sözdizimi, bir fonksiyonun çok uzun bir tür yazmaya gerek kalmadan Iterator trait’ini uygulayan bir tür döndürdüğünü özlü bir şekilde belirtmenizi sağlar.
Ancak, impl Trait’i yalnızca tek bir tür döndürüyorsanız kullanabilirsiniz. Örneğin, dönüş türü impl Ozet olarak belirtilen ve bir HaberMakalesi ya da bir SosyalGonderi döndüren bu kod işe yaramayacaktır:
pub trait Ozet {
fn ozetle(&self) -> String;
}
pub struct HaberMakalesi {
pub manset: String,
pub konum: String,
pub yazar: String,
pub icerik: String,
}
impl Ozet for HaberMakalesi {
fn ozetle(&self) -> String {
format!("{}, {} ({})", self.manset, self.yazar, self.konum)
}
}
pub struct SosyalGonderi {
pub kullanici_adi: String,
pub icerik: String,
pub yanit: bool,
pub yeniden_paylasim: bool,
}
impl Ozet for SosyalGonderi {
fn ozetle(&self) -> String {
format!("{}: {}", self.kullanici_adi, self.icerik)
}
}
fn ozetlenebilir_dondur(degistir: bool) -> impl Ozet {
if degistir {
HaberMakalesi {
manset: String::from(
"Penguenler Stanley Cup Şampiyonluğunu kazandı!",
),
konum: String::from("Pittsburgh, PA, ABD"),
yazar: String::from("Iceburgh"),
icerik: String::from(
"Pittsburgh Penguenleri bir kez daha NHL'deki en iyi \
hokey takımı.",
),
}
} else {
SosyalGonderi {
kullanici_adi: String::from("horse_ebooks"),
icerik: String::from(
"elbette, muhtemelen zaten bildiğiniz gibi, insanlar",
),
yanit: false,
yeniden_paylasim: false,
}
}
}
Derleyicide impl Trait sözdiziminin nasıl uygulandığına ilişkin kısıtlamalar nedeniyle HaberMakalesi veya SosyalGonderi döndürülmesine izin verilmez. Bu davranışa sahip bir fonksiyonun nasıl yazılacağını Bölüm 18’in “Paylaşılan Davranış Üzerinden Soyutlama Yapmak İçin Trait Nesneleri Kullanmak” bölümünde ele alacağız.
Metotları Koşullu (Conditionally) Uygulamak İçin Trait Sınırlarını Kullanmak
Jenerik tür parametreleri kullanan bir impl bloğu ile birlikte bir trait sınırı kullanarak, belirtilen traitleri uygulayan türler için koşullu olarak metotlar uygulayabiliriz. Örneğin, Liste 10-15’teki Cift<T> türü her zaman yeni bir Cift<T> örneği döndüren new fonksiyonunu uygular (Bölüm 5’in “Metot Sözdizimi” kısmından hatırlayın, Self, bu durumda Cift<T> olan impl bloğunun türü için bir tür takma adıdır). Fakat bir sonraki impl bloğunda, Cift<T> yalnızca kendi içindeki T türü karşılaştırmayı sağlayan PartialOrd trait’ini ve yazdırmayı sağlayan Display trait’ini uyguluyorsa karsilastir_goster metodunu uygular.
use std::fmt::Display;
struct Cift<T> {
x: T,
y: T,
}
impl<T> Cift<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T: Display + PartialOrd> Cift<T> {
fn karsilastir_goster(&self) {
if self.x >= self.y {
println!("En büyük üye x = {}", self.x);
} else {
println!("En büyük üye y = {}", self.y);
}
}
}
Ayrıca, başka bir trait’i uygulayan herhangi bir tür için koşullu olarak bir trait de uygulayabiliriz. Trait sınırlarını karşılayan herhangi bir tür üzerindeki trait uygulamalarına kapsayıcı uygulamalar denir ve Rust standart kütüphanesinde yaygın olarak kullanılırlar. Örneğin, standart kütüphane Display trait’ini uygulayan her tür için ToString trait’ini uygular. Standart kütüphanedeki impl bloğu bu koda benzemektedir:
impl<T: Display> ToString for T {
// --snip--
}
Standart kütüphanede bu kapsayıcı uygulama (blanket implementation) bulunduğundan dolayı, Display trait’ini uygulayan herhangi bir tür üzerinde ToString trait’i tarafından tanımlanan to_string metodunu çağırabiliriz. Örneğin tamsayılar Display uyguladığından tamsayıları bu şekilde karşılık gelen String değerlerine dönüştürebiliriz:
#![allow(unused)]
fn main() {
let s = 3.to_string();
}
Kapsayıcı uygulamalar, trait’in dokümantasyonunda “Uygulayıcılar” (Implementors) bölümünde yer alır.
Traitler ve trait sınırları, kod tekrarını azaltmak için jenerik tür parametrelerini kullanan kod yazmamızı sağlarken aynı zamanda derleyiciye jenerik türün belirli bir davranışa sahip olmasını istediğimizi belirtmemize de olanak tanır. Derleyici daha sonra trait sınırı bilgisini, kodumuzla birlikte kullanılan tüm somut türlerin doğru davranışı sağladığını kontrol etmek için kullanabilir. Dinamik olarak yazılmış dillerde, bir metodu tanımlamayan bir tür üzerinde metodu çağırsaydık çalışma zamanında bir hata alırdık. Ancak Rust bu hataları derleme zamanına taşır, böylece kodumuz çalışmadan önce sorunları düzeltmeye zorlanırız. Ek olarak, çalışma zamanında davranış için kontrol yapan bir kod yazmamıza gerek kalmaz, çünkü zaten derleme zamanında kontrol etmişizdir. Bunu yapmak jeneriklerin esnekliğinden vazgeçmek zorunda kalmadan performansı artırır.