- Bir trait bildirmek ve onu farklı türler için uygulamak
- Trait sınırlı generic fonksiyonlar yazmak,
impl Traitvewherekullanmak Debug,ClonevePartialEq'yu#[derive]ile elde etmek- E0106 ve E0597 hatalarını yaşam süreleriyle açıklamak
Telefon, dizüstü bilgisayar ve kulaklık birbirinden tamamen farklı cihazlardır ama hepsi aynı USB-C kablosuyla şarj olur. Kablo onların içini bilmez; onun için önemli olan yalnızca “şarj olabilme” yeteneğidir. Rust'ta bu ortak yeteneğe trait denir. Trait'ler, generic türlerle birlikte bir kez yazılan kodun birçok farklı türle çalışmasını sağlar.
Trait'ler
Trait, bir türün sahip olması gereken metotların listesidir. impl TraitAdı for Tür bloğu bu metotları somut bir tür için yazar. Trait, bir metodun varsayılan gövdesini de verebilir: tür onu olduğu gibi bırakabilir ya da kendi sürümüyle değiştirebilir. Display, Debug ve Clone, standart kütüphanenin trait'leridir.
trait Describe {
fn name(&self) -> String;
fn describe(&self) -> String {
format!("This is {}", self.name())
}
}
struct Cat;
struct Car {
model: String,
}
impl Describe for Cat {
fn name(&self) -> String {
String::from("a cat")
}
}
impl Describe for Car {
fn name(&self) -> String {
format!("a car ({})", self.model)
}
fn describe(&self) -> String {
format!("Beep! {}", self.name())
}
}
fn main() {
let car = Car { model: String::from("sedan") };
println!("{}", Cat.describe());
println!("{}", car.describe());
}This is a cat Beep! a car (sedan)
Cat, varsayılan describe metodunu olduğu gibi bıraktı; Car ise onu kendi sürümüyle değiştirdi. struct Cat; alanı olmayan bir yapıdır.Generic türler ve trait sınırları
Generic bir fonksiyon, somut bir tür yerine bir tür parametresi alır: fn largest<T>(...). Ama T hakkında hiçbir şey bilinmiyorsa onunla hiçbir şey yapılamaz, karşılaştırma bile. Trait sınırı (T: PartialOrd + Copy) şunu söyler: “T, karşılaştırılabilen ve kopyalanabilen herhangi bir türdür”. Derleyici her somut tür için fonksiyonun ayrı bir kopyasını oluşturur; bu yüzden generic kod çalışma sırasında yavaşlamaz.
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
let mut max = items[0];
for &item in items {
if item > max {
max = item;
}
}
max
}
fn main() {
println!("{}", largest(&[3, 9, 2]));
println!("{}", largest(&[1.5, 0.5]));
println!("{}", largest(&['a', 'z', 'm']));
}9 1.5 z
fn largest<T>(items: &[T]) -> T {
let mut max = items[0];
for &item in items {
if item > max {
max = item;
}
}
max
}fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
let mut max = items[0];
for &item in items {
if item > max {
max = item;
}
}
max
}binary operation > cannot be applied to type T yazar ve T: PartialOrd eklemeyi önerir. Copy ise elemanı listeden kopyalayarak almak için gereklidir.Daha kısa yazmak için parametrede impl Trait kullanılabilir: item: &impl Display, “Display'i uygulayan herhangi bir tür” demektir. Sınırlar çok olduğunda onları imzadan sonra bir where bloğuna taşımak okunaklılığı artırır. impl Trait dönüş türü olarak da yazılabilir; örneğin uzun bir yineleyici türünün adını yazmamak için.
use std::fmt::Display;
fn announce(item: &impl Display) {
println!("[{item}]");
}
fn show_pair<T, U>(a: T, b: U)
where
T: Display,
U: Display,
{
println!("{a} & {b}");
}
fn evens(limit: u32) -> impl Iterator<Item = u32> {
(1..=limit).filter(|n| n % 2 == 0)
}
fn main() {
announce(&42);
announce(&"hello");
show_pair("Rust", 2015);
let v: Vec<u32> = evens(10).collect();
println!("{:?}", v);
}[42] [hello] Rust & 2015 [2, 4, 6, 8, 10]
#[derive] ile hazır trait'ler
Birçok standart trait'i elle yazmak gerekmez: #[derive(...)] özniteliği onları derleyiciye otomatik olarak ürettirir. Bunun için yapının tüm alanlarının da bu trait'leri uygulaması gerekir.
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let a = Point { x: 1, y: 2 };
let mut b = a.clone();
println!("{:?} == {:?}: {}", a, b, a == b);
b.x = 5;
println!("{:?} == {:?}: {}", a, b, a == b);
}Point { x: 1, y: 2 } == Point { x: 1, y: 2 }: true
Point { x: 1, y: 2 } == Point { x: 5, y: 2 }: false| Trait | Ne sağlar |
|---|---|
Debug | {:?} ve {:#?} ile yazdırma |
Clone | .clone() ile tam kopya |
Copy | taşıma yerine otomatik kopyalama (yalnızca basit alanlarla) |
PartialEq, Eq | == ve != ile karşılaştırma |
PartialOrd, Ord | <, > ve sıralama |
Hash | HashMap anahtarı olarak kullanım |
Default | Type::default() ile varsayılan değer |
Yaşam sürelerinin temelleri
Her referansın bir yaşam süresi (lifetime) vardır: referansın geçerli olduğu kod bölümü. Çoğu zaman derleyici bunu kendisi çıkarır. Ama bir fonksiyon iki referans alıp birini döndürdüğünde derleyici sonucun hangisinden ödünç aldığını bilemez. 'a açıklaması ilişkiyi tanımlar: “sonuç, iki girdiden de uzun yaşamaz”. Açıklama değerlerin ömrünü değiştirmez, yalnızca derleyiciye açıklar.
fn longest(a: &str, b: &str) -> &str {
if a.len() >= b.len() {
a
} else {
b
}
}fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() {
a
} else {
b
}
}missing lifetime specifier — this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from a or b. Sağdaki imzayı derleyicinin kendisi önerir.fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() {
a
} else {
b
}
}
fn main() {
let city = String::from("Lankaran");
let result;
{
let other = String::from("Baku");
result = longest(&city, &other);
println!("longest: {result}");
}
println!("{}", longest("Quba", "Shaki"));
}longest: Lankaran Shaki
println!("longest: {result}") satırını iç bloğun dışına taşırsak kod artık derlenmez. Burada sonuç aslında city'dir; ama imzaya göre other'dan da ödünç almış olabilir ve other bloğun sonunda düşürülür:
error[E0597]: `other` does not live long enough
--> src/main.rs:14:33
|
13 | let other = String::from("Baku");
| ----- binding `other` declared here
14 | result = longest(&city, &other);
| ^^^^^^ borrowed value does not live long enough
15 | }
| - `other` dropped here while still borrowed
16 | println!("longest: {result}");
| -------- borrow later used hereÖnemli noktalar
- Trait ortak davranışı tanımlar;
impl Trait for Türonu somut bir tür için uygular, varsayılan metotlar ise değiştirilebilir. - Generic fonksiyon
<T: Sınır>ile yazılır; sınır,Tile neler yapılabileceğini belirtir. - Derleyici her tür için ayrı kod üretir; bu yüzden generic kod hız kaybetmez.
#[derive(Debug, Clone, PartialEq)], sık gereken trait'leri otomatik olarak üretir.'ayaşam süresi açıklaması referanslar arasındaki ilişkiyi tanımlar, ömrü değiştirmez.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.