Ez a kvíz a modul eddigi anyagát fedi le: a trait-ek és impl blokkok, a default metódusok, a dyn Trait és a statikus/dinamikus dispatch, a generikák és trait bound-ok, a monomorfizáció, valamint az Iterator trait és a saját iterátor implementálása. Ne siess, próbáld meg fejben (vagy papíron) megválaszolni a kérdést, mielőtt kinyitod a megoldást.
Trait-ek, impl blokkok, default metódusok
1. kérdés. Mit ír ki ez a kód?
trait Greet {
fn name(&self) -> String;
fn greet(&self) -> String {
format!("Szia, {}!", self.name())
}
}
struct Dog;
impl Greet for Dog {
fn name(&self) -> String {
"Buksi".to_string()
}
}
fn main() {
let d = Dog;
println!("{}", d.greet());
}
Megoldás
"Szia, Buksi!" — a `Dog` csak a `name` metódust implementálja, a `greet` default metódust a trait definíciója adja, és az felhasználja a `name` eredményét.2. kérdés. Melyik állítás igaz a default metódusokra?
A) Minden implementáló típusnak felül kell írnia. B) Csak akkor futnak, ha a típus nem implementál semmit. C) Használhatók anélkül is, hogy egy típus felülírná őket, de felülírhatók. D) Nem hívhatnak más trait-metódust.
Megoldás
C) A default metódus a trait törzsében kap implementációt, ezt bármelyik `impl` felülírhatja, de nem kötelező.dyn Trait és dispatch
3. kérdés. Fordul-e ez a kód?
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![Box::new(Circle { radius: 2.0 })];
for s in &shapes {
println!("{:.2}", s.area());
}
}
Megoldás
Igen, lefordul, és `12.57`-et ír ki. A `Shape` object-safe trait, tehát `Box4. kérdés. Mi a fő különbség a statikus és a dinamikus dispatch között?
Megoldás
Statikus dispatch (generikák, monomorfizáció) esetén a fordító már fordításkor tudja, melyik konkrét típus metódusát kell hívni, ezért nincs futásidejű overhead. Dinamikus dispatch (`dyn Trait`) esetén ez futásidőben, egy vtable-en keresztül derül ki, ára van egy kis teljesítményveszteség, de cserébe egy gyűjteményben különböző típusok is elférnek.Generikák, trait bound, monomorfizáció
5. kérdés. Fordul-e ez a kód, és ha nem, miért?
fn largest<T>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
Megoldás
Nem fordul: a `>` operátor a `PartialOrd` trait-et igényli, de a `T` típusparaméterre nincs trait bound megadva. A javítás: `fn largest6. kérdés. Mire jó a where klóz a trait bound-ok megadásakor?
Megoldás
Ugyanazt fejezi ki, mint a `7. kérdés. Mi a monomorfizáció?
A) Futásidőben a generikus függvényből egyetlen közös kód generálódik.
B) Fordításkor a fordító minden konkrét típushoz külön, specializált kódot generál a generikus függvényből.
C) A dyn Trait másik neve.
D) A trait-öröklés szinonimája.
Megoldás
B) A monomorfizáció miatt a generikus kód gyakorlatilag ugyanolyan gyors, mint ha kézzel írtad volna meg típusonként — ez az ára a nagyobb bináris méretnek.Iterator trait
8. kérdés. Mit ír ki ez a kód?
fn main() {
let v = vec![1, 2, 3, 4, 5];
let sum: i32 = v.iter().filter(|&&x| x % 2 == 0).map(|x| x * 10).sum();
println!("{}", sum);
}
Megoldás
`60` — a `filter` a páros elemeket (2 és 4) tartja meg, a `map` mindegyiket megszorozza 10-zel (20, 40), a `sum` pedig összeadja őket.9. kérdés. Egy saját típushoz Iterator implementálásakor melyik metódust kötelező megadni, és mit kell visszaadnia?
Megoldás
A `next(&mut self) -> OptionHogyan tovább
Ha a 3–4. és 7. kérdésnél elakadtál, érdemes visszalapozni a "Statikus vs dinamikus dispatch" és a "Generikák és trait bound" leckékhez. Ha a 8–9. kérdés nehezen ment, nézd át újra a "Saját iterátor írása" leckét. A következő leckében a lifetime annotációkkal ismerkedsz meg — azzal a szintaxissal, amivel a fordítónak jelzed, hogy meddig érvényesek a referenciáid.