A Rust programozási nyelv

Alprogramok

Szintaxis

Függvényeket megadhatunk globális vagy akár beágyazhatunk más függvények törzsébe. Az fn kulcsszó vezeti be, van neve és paraméterlistája, ebben név : típus párok vannak, „->” után visszatérési érték és törzs:

fn ( : , : ) → t3 {}
fn line(a: int, b: int, x: int) -> int { return a * x + b; }

A visszatérési értéket a függvény törzsében két -féle képpen adhatjuk meg: return kulcsszó után, ezt követi egy kifejezés vagy a blokk utolsó kifejezése után nem teszünk „;”-t és akkor ennek az értéke lesz a visszatérési érték. Azok a függvények amelyeknek nincs visszatérési értéke nil-el („()”) térnek vissza.

fn do_nothing_the_hard_way() -> () { return (); } fn do_nothing_the_easy_way() { }

Továbbá támogatja a mintaillesztést, pl.: egy tuple első értékét adja vissza:

fn first((value, _): (int, f64)) -> int { value }

Túlterhelés

Függvények túlterhelése trait-ekkel lehetséges:

enum input { int(int), str(str) } trait to_input { fn to_input() -> input; } impl to_input for int { fn to_input() -> input { return int(self); } } impl to_input for str { fn to_input() -> input { return str(self); } } fn to_input(t: T) { match t.to_input() { int(v) { println("int"); } str(v) { println("str"); } } } fn main() { to_input(5); to_input("hello") }

Closure

A nevesített függvények esetében a függvény törzsében nem lehet használni azon kívül megadott változókat. A Rust erre is ad lehetőséget, ezeket closure nevezzük, hozzáférhetnek a legközelebbi őt tartalmazó hatáskör változtozóihoz.

fn call_closure_with_ten(b: |int|) { b(10); } let captured_var = 20; let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg); call_closure_with_ten(closure);

A closure-ket a paraméterlistában két „|” közé kell írni. A típust közé nem kell általában kiírni mert a fordító ki tudja következtetni.

Több fajta lezárt van, különböző szabályokkal. A leggyakoribb a „stack closure”, két „|” között, közvetlenül tud hozzáférni a külső változókhoz.

let mut max = 0; [1, 2, 3].map(|x| if *x > max { max = *x });

Ezek nagyon hatékonyak mivel, a környezetük allokálva van a call stack-en és a lokális változókra referenciákon keresztül hivatkoznak. Sosem élnek tovább, mint a változók amire hivatkoznak. Kizárólag argumentum pozícióban használhatóak, nem tárolhatóak adatszerkezetekben és nem lehet függvény visszatérési értéke.

A lezártaknak bármilyen típust átlehet adni, kivétel „||”. Vagyis, ha írunk egy magasabb rendű függvényt ami csak hívja a függvény argomentumait, és nem csinál mást velük, akkor akkor is kell deklarálni egy „||” típusú argomentumot. Ekkor a hívó tetszőleges típussal tudja hívni.

fn call_twice(f: ||) { f(); f(); } let closure = || { "I'm a closure, and it doesn't matter what type I am"; }; fn function() { "I'm a normal function"; } call_twice(closure); call_twice(function);

Metódusok

Hasonlóan a függvényekhez, egy speciális paraméterrel kezdődnek ez a „self” és fogadó objektumnak megfelelő típusú, arra mutat, hasonlóan, mint C++-ban a this. Az „impl” kulcsszóval kezdődik a metódus implementációja. Enum-ok és struct-okhoz adhatunk metódusokat. Egy objektumon a . operátorral lehet meghívni, pl.: a.draw(). Például:

struct Point { x: f64, y: f64 } enum Shape { Circle(Point, f64), Rectangle(Point, Point) } impl Shape { fn draw(&self) { match *self { Circle(p, f) => draw_circle(p, f), Rectangle(p1, p2) => draw_rectangle(p1, p2) } } } let s = Circle(Point { x: 1.0, y: 2.0 }, 3.0); s.draw();

Az, hogy milyen típusként használjuk a self paramétert a metódustól függ, írhatjuk self, &self, @self vagy ~self. Így a hivó felelőssége, hogy megfelelő mutatóra hívja meg a metódust.