A Rust programozási nyelv

Típusok, típuskonstrukciók

Elemi típusok

Vannak előjeles és nélküli integer(egész) típusok, int és uint, ezeknek 8,16,32,64 bites variánsai, i8, u16, stb. Megadhatjuk őket 2,8,10,16 számrendszerben is. Minden integrál típusnak van suffix literálja, amiből kikövetkeztethető a típusa: i → int, u → uint stb. Ha nem adunk meg ilyen suffixeket a fordító kikövetkezteti, hogy milyen integer típusú, a típusinformációkból és függvény szignatúrákból a program környező részein. Ha nincs semmilyen információja akkor a típusa int lesz.

let a = 1; let b = 10i; let c = 100u; let d = 1000i32;

Lebegő pontos számokból két változat áll rendelkezésünkre: f32 és f64. A lebegőpontos számokat 0.0, 1e6 vagy 2.1e-4 formában adhatjuk meg. Az integerekhez hasonlóan a suffix literálból lehet következtetni a pontos típusra: f32 és f64 használható suffixnek.

Továbbá van még bool típus is, értékei lehetnek: true és false.

Karatertípus, char, 4 bájtos Unicode, amely literáljait aposztróf közé írjuk: 'x'. Hasonlóan, mint C-ben kezeli az escape karaktereket: \n\t\r.

Van még egy unit típus, amit () adhatunk meg, egy értéke van (nil): ().

Típusszerkezetek

Struktúrák

A strukúrákat definiálni kell használat előtt, szintaxis:

struct Name { field1: T1, field2: T2 [, ...] } ahol T1,T2.. típusok.

Létrehozásnál elhagyjuk a struct kulcsszót:

Point { x: 1.0, y: 2.0 }

„Inherited mutability” tulajdonsággal rendelkeznek(örökölt változtathatóság), vagyis az adattagjai megváltoztathatóak ha a struct-ot úgy adtuk meg a deklarációban. A adattagokra a név.tag formában hivakozunk.

let mut mypoint = Point { x: 1.0, y: 1.0 }; let origin = Point { x: 0.0, y: 0.0 }; mypoint.y += 1.0; origin.y += 1.0; //ERROR

Mintaillesztésben is megengedett a használatuk:

match mypoint { Point { x: 0.0, y: yy } => { println(yy.to_str()); } Point { x: xx, y: yy } => { println(xx.to_str() + " " + yy.to_str()); } }

Ekkor a neveket nem kell a megadás sorrendjében felsorolni, sőt ha vannak olyanok amiket nem illesztünk akkor azok elhagyhatóak, ekkor a végére kell még egy „,..” rész.

match mypoint { Point { x, .. } => { println(x.to_str()) } }
Felsorolási típus

Adattípusok melyeknek, több alternatív reprezentációja van.

enum Shape { Circle(Point, f64), Rectangle(Point, Point) }

Ennek a típusnak a lehetséges értékei, Circle: ami tartalmaz egy Point és f64 típust, illetve Rectangle, ami két Point-t tartalmaz. A futás idejű reprezentációjuk egy azonosítója az aktuális alaknak, hasonlóan a C-beli „tagged union” mintához. Létrehozás:

Circle(Point { x: 0.0, y: 0.0 }, 10.0)

Paraméter nélküli enum:

enum Direction { North, East = 9, South, West }

Ekkor North, East, South, West konstansok, melyek típusai Direction. C-hez hasonlóan lehetőség van megkülönböztető értékekkel ellátni az enum elemeit. Ha egy elemhez nem adunk meg akkor annak az értéke az előző +1. Az első alapértelmezetten 0. Az as operátorral lehetséges egy enum elemét int-re konvertálni.

Az elemek konstruktorai felhasználhatóak mintaillesztésben is:

use std::f64; fn area(sh: Shape) -> f64 { match sh { Circle(_, size) => f64::consts::PI * size * size, Rectangle(Point { x, y }, Point { x: x2, y: y2 }) => (x2 - x) * (y2 - y) } }

Az elemei lehetnek struct-ok is:

use std::f64; enum Shape { Circle { center: Point, radius: f64 }, Rectangle { top_left: Point, bottom_right: Point } } fn area(sh: Shape) -> f64 { match sh { Circle { radius: radius, .. } => f64::consts::PI * square(radius), Rectangle { top_left: top_left, bottom_right: bottom_right } => { (bottom_right.x - top_left.x) * (top_left.y - bottom_right.y) } } }
Tuple

Hasonlóak a struct-okhoz, kivéve hogy a tagjainak nincsenek nevei vagyis nem férhetünk hozzájuk a „.” operátorral. Lehet akármekkora az aritásuk kivéve 0.

let mytup: (int, int, f64) = (10, 20, 30.0); match mytup { (a, b, c) => info!("{}", a + b + (c as int)) }
Tuple struct

A struct-ok és tuple-k keveréke. Rendelkezik névvel, de a mezőinek nincsenek nevük(a struct-oknál van), pl.: Foo(1,2) nem egyenlő Bar(1,2).

struct MyTup(int, int, f64); let mytup: MyTup = MyTup(10, 20, 30.0); match mytup { MyTup(a, b, c) => info!("{}", a + b + (c as int)) }

Speciális eset amikor egy mező van, ekkor egy új típus jön létre a reprezentációja megegyezik azzal amiből létrejött de nem ekvivalensek.

struct Inches(int); struct Centimeters(int);

Mutató típusok

Rust-ban több fajta mutató típus van, amiknek különböző tulajdonságaik vannak:

Owned pointer

A legegyszerűbb mutató típus, csak egy mutathat egy memória területre A fordító a memória kezeléshez, statikus analízist végez a mutatókon, hogy eldöntse mikor kerülnek hatáskörbe és mikor hagyják el azt.

Managed pointer

Az utolsó verzióban ez már deprecated, helyette az Rc és Gc mutatók használatosak. Rc: referencia számlálós. Több managed pointer is mutathat azonos memória területre, de az csak akkor szabadul majd fel, ha már egy sem mutat rá. Gc: aktiválja a Rust Garbage collectort.

Refrencia

A többivel ellentétben nem birtokolja az objektumot, csak mutat rá. Freezing: Amikor egy nem változtatható referenciát adunk értékül akkor a értékül adott objektum nem változtatható amíg vissza nem kapjuk. Itt x nem használható amíg y érvényben van:

let mut x = 5; {     let y = &x;  }

Dereferálás

Minden mutatót a * operátorral dereferálhatunk. A változtatható mutatóknál, a dereferált mutató állhat az értékadás bal oldalán. A precedenciája kisebb, mint a .-nak A . és [] operátorok automatikusan defereferálnak akár milyen mélyen

let start = @Point { x: 10.0, y: 20.0 }; let end = ~Point { x: start.x + 100.0, y: start.y + 100.0 }; let rect = &Rectangle(*start, *end); let area = rect.area(); let point = &@~Point { x: 10.0, y: 20.0 }; println!("{:f}", point.x);

Move szemantika

A Rust alapértelmezetten shallow copy-t alkalmaz a paraméterátadásra, értékadáshoz és függvények visszatérési értékénél. Move: Ha egy olyan objektumot másolunk, amelyik rendelkezik destruktorral, akkor másolásnál az objektum tulajdonjoga is tovább adódik és a másolt objektumot nem tudjuk használni amíg újra nem inicializáljuk. Ennek elkerülésére használható a Clone. A „Clone” metódust a Clone „trait” biztosítja.

let x = ~5; let y = x.clone(); let z = x;
„y”-nak új hely foglalódik és az „x” nem nem használható újra inicializálás nálkül tovább

Vektorok

A vektor egy a memóriában folytonosan elhelyezkedő 0-t vagy azonos típusú elemeket tartalmazó típus. Továbbá támogatva van a vektor mutatók is, amelyeket „slice”-nak hívnak. Az elemekre a [] operátorral lehet hivatkozni. Továbbá számos funkcióval rendelkeznek, mint push, remove stb.

A string-eket u8 elemek vektoraként reprezentáljuk Rust-ban, és garantálva van, hogy egy érvényes UTF-8 sorozat.

A fix méretű vektorok „unboxed”, az értékek közvetlenül tárolódnak benne, az elemszám része a típusnak. Tehát ha vektor változtatható akkor az elemei is. Fix méretű stringek nem léteznek.

// A fixed-size vector let numbers = [1, 2, 3]; let more_numbers = numbers; // The type of a fixed-size vector is written as `[Type, ..length]` let five_zeroes: [int, ..5] = [0, ..5];

A „unique” vektorok dinamikusan méretezhetőek, és van egy destruktoruk ami majd felszabadítja az általuk lefoglalt memóriát. Az elemeket közvetlenül tartalmazza, tehát a változtathatóság, mint előbb.

// A dynamically sized vector (unique vector) let mut numbers = ~[1, 2, 3]; numbers.push(4); numbers.push(5); // The type of a unique vector is written as ~[int] let more_numbers: ~[int] = numbers; // The original `numbers` value can no longer be used, due to move semantics. let mut string = ~"fo"; string.push_char('o');

A „slice”-ok hasonlók a fix méretű vektorokhoz, de a méretük nem része a típusnak. Egyszerűen mutatókat tartalmaznak elemekre, nem birtokolják a tartalmazott elemeket.

// A slice let xs = &[1, 2, 3]; // Slices have their type written as &[int] let ys: &[int] = xs; // Other vector types coerce to slices let three = [1, 2, 3]; let zs: &[int] = three; // An unadorned string literal is an immutable string slice let string = "foobar"; // A string slice type is written as &str let view: &str = string.slice(0, 3);

Változtatható „slices”-ok is vannak, ekkor az elemeket kell változtathatóként megadni.

let mut xs = [1, 2, 3]; let view = xs.mut_slice(0, 2); view[0] = 5; // The type of a mutable slice is written as &mut [T] let ys: &mut [int] = &mut [1, 2, 3];

Változók, konstansok

A let kulcsszó egy lokális változót vezet be, ekkor alapértelmezetett konstansok. Ahhoz, hogy később tudjuk változtatni a mut kulcsszót kell használni.

let hi = "hi"; let mut count = 0; while count < 10 { println!("count is {}", count); count += 1; }

A lokális változók típusát a fordító kikövetkezteti, de ha akarjuk megadható a let név: típusnév formában. Definiálhatunk statikus változókat is ekkor kötelező megadnia típust.

static MONSTER_FACTOR: f64 = 57.8; let monster_size = MONSTER_FACTOR * 10.0; let monster_size: int = 50;

A változóknál itt is van elfedés(shadowing), a második monster_size elfedi az elsőt, attól függetlenül, hogy az első típusa f64 a másodiké pedig int. Ennél a kódnál „unused variable” warningot kapunk, ennek elkerülésére, ha a változók nevét _-al kezdjük akkor nem ad rá ilyen warning-ot a fordító akkor sem ha kellene.

let _monster_size = 50;

Kifejezések, operátorok

Jelentősebb különbség a szintaxisban a C-hez képest, hogy több konstrukció ami utasítás C-ben, az kifejezés Rust-ban, hogy tömörebb kódot írhassunk.

let price = if item == "salad" { 3.50 } else if item == "muffin" { 2.25 } else { 2.00 };

A blokkokban nincs pontosvessző, mert a blokkok utolsó kifejezésének értéke lesz a blokk értéke. Ez használható több helyen ahol, nem deklarációt adunk meg (let, fn, stb.), hanem egy kifejezést, például függvény törzset.

fn is_four(x: int) -> bool { x == 4 }

Az utolsó kifejezés értéke lesz a függvény visszatérési értéke.

Az operátoroknál kevés újdonság van. Aritmetikus operátorok *, /, %, +, -. Bitszintű operátorok, mint C-ben <<, >>, &, |, ^, !(not). Összehasonlító operátorok ==, !=, >, <, >=, <=. Lusta kiértékelésű bool operátorok && és ||. A fordítási idejű castolásokhoz használható az as operator. Bal oldalon egy kifejezést vár és jobb oldalon egy típust, ha lehetséges akkor a kifejezés értékét konvertálja a jobb oldalon megadottra. Általában a primitív numerikus típusokra és mutatókra szokás alkalmazni. Ez nem felüldefiniálható. Az operátorok precedenciája, mint C-ben. Továbbá transmute hasznáható nem biztonságos C-szerű castolásokhoz azonos méretű típusokon.

let x: f64 = 4.0; let y: uint = x as uint; assert!(y == 4u);