A Rust programozási nyelv

Generikusok

Bevezetés

Típus paraméterrel definiálhatunk függvényeket és típusokat. Egyszerűen a definíciónál név után "<> " közé kell írni a típusparamétereket. Példányosításnál nem szükséges kiírni a típusokat, mert a fordító ki tudja következtetni. A típus változók átlátszatlanok: korlátozott műveleteket lehet végezni és nem használhatóak minta illesztésben.

use std::hashmap::HashMap; type Set = HashMap; struct Stack {     elements: ~[T] } enum Option {     Some(T),     None }    fn print_vec(v: &[T]) {         for i in v.iter() {             println((*i).to_str())         }     }     fn main() {         let vec = [1,2,3];         print_vec(vec);         let str_vec = [~"hey", ~"there", ~"yo"];         print_vec(str_vec);     }

A fordító hatékonyan fordítja, monomorphizing: Minden generikus függvény hívásához generál egy külön másolatot és a konkrét híváshoz optimalizálja Hasonló hatékonyság, mint a C++ template-k.

Trait

Korlátozza a generikus típusparamétereket. Leszűkíti a típusparaméterek lehetséges értékeit aszerint, hogy milyen függvényeket kell megvalósítaniuk, ezáltal biztonságosabbé téve a használatukat.

Például ha van egy generikus metódusunk T típusparaméterrel, és vár egy T típusú paramétert, akkor ha azt a T típusú paramétert másolni akarjuk a függvény törzsében a fordító jelezni fog, hogy ez nem megy. Ahhoz, hogy működjön megkell határozni, hogy a T típus megvalósítja a Clone trait-et:

fn head_bad(v: &[T]) -> T { v[0] // error } fn head(v: &[T]) -> T { v[0].clone() // ok }

Számos beépített trait van: pl. Clone (másolhatóság), Drop (destruktor).

Felhasználó trait

A beépített trait-eken kívül, mi is megadhatunk új trait-eket. Először megkell adni a trait szignatúráját, benne a megvalósított függvényekkel.

trait Printable { fn print(&self); }

Majd a típushoz megkell adni az implementációt egy impl blokkban, mint a metódusoknál. Használni pedig például így: "a".print()

impl Printable for int { fn print(&self) { println!("{:?}", *self) } } impl Printable for ~str { fn print(&self) { println!("{}", *self) } }
Alapértelemzett definíció

Sokszor előfordulhat, hogy egy trait funkciójának megvalósítása sok típusra ugyan az lesz. Ekkor a kódismétlés elkerülésére megadhatunk a trait definíciójakor egy alapértelmezett függvény definíciót amit akkor használunk amikor egy típushoz az implementációs blokk üres. De ha szükséges nyílván felül is lehet definiálni.

trait Printable { fn print(&self) { println!("{:?}", *self) } } impl Printable for int {} impl Printable for ~str { fn print(&self) { println!("{}", *self) } } impl Printable for bool {} impl Printable for f32 {}
Típus paraméterett trait

Trait-eket is adhatunk meg típusparaméterrel. Ekkor ezt a típus paramétert a trait által biztosított függvényekben felhasználhatjuk.

trait Seq { fn length(&self) ­> uint; } impl Seq for ~[T] { fn length(&self) ­> uint { self.len() } }

Illetve a típusparamétert nem is feltétlenül kell kiírni, erre van egy speciális Self típus.

trait Eq { fn equals(&self, other: &Self) ­> bool; } impl Eq for int { fn equals(&self, other: &int) ­> bool { *other == *se }

Static metódusokat is megadhatunk, ha az első paraméter nem self. Ezeket a trait név::függvény névvel érhetjük el.

use std::f64::consts::PI; trait Shape { fn new(area: f64) -> Self; } struct Circle { radius: f64 } struct Square { length: f64 } impl Shape for Circle { fn new(area: f64) -> Circle { Circle { radius: (area / PI).sqrt() } } } impl Shape for Square { fn new(area: f64) -> Square { Square { length: area.sqrt() } } } let area = 42.5; let c: Circle = Shape::new(area); let s: Square = Shape::new(area);
Trait öröklődés

Trait öröklődhet másikból. a leszármazott trait-et megvalósító típusnak, implementálnia kell a bázis trait-et is. Polimorfikusan használhatóak: ahol bázis trait-et várnának ott adhatunk leszármazottat is. Többszörös öröklődés van, +-al kell elválasztani a bázisokat. Szintaxis: trait név után kell írni a bázis trait-et.

use std::f64::consts::PI; trait Shape { fn area(&self) ­> f64; } trait Circle : Shape { fn radius(&self) ­> f64; } struct CircleStruct { center: Point, radius: f64 } impl Circle for CircleStruct { fn radius(&self) ­> f64 { (self.area() / PI).sqrt() } } impl Shape for CircleStruct { fn area(&self) ­> f64 { PI * square(self.radius) } } fn radius_times_area(c: T) -> f64 { // `c` is both a Circle and a Shape c.radius() * c.area() } fn main() { let concrete = ~CircleStruct{center:Point{x:3.0,y:4.0},radius:5.0}; let mycircle: ~Circle = concrete as ~Circle; let nonsense = mycircle.radius() * mycircle.area(); }
Implemetáció származatatás

Az std és extra könyvtárban található trait-ekből néhányhoz lehet az implementációt származtatni a saját típusainknak: Eq, TotalEq, Ord, TotalOrd, Encodable Decodable, Clone, DeepClone, IterBytes, Rand, Default, Zero, ToStr

#[deriving(Eq)] struct Circle { radius: f64 } #[deriving(Rand, ToStr)] enum ABC { A, B, C }