Az előző leckében megtanultad, hogy a let és a mut hogyan viselkedik, és mit jelent a shadowing. Most egy lépéssel tovább megyünk: megnézzük, milyen típusú értékeket tehetünk egyáltalán egy változóba. A Rust erősen és statikusan típusos nyelv, ami azt jelenti, hogy minden érték típusa fordítási időben ismert és rögzített — ez a szigorúság az, ami később (amikor eljutunk az ownershiphez) segít elkerülni egész hibaosztályokat. Kezdjük a legegyszerűbb, úgynevezett skalár típusokkal, majd nézzük meg, hogyan tudunk több értéket egy csomagba rendezni tuple-ökkel és array-ekkel, végül tisztázzuk a szöveges típusok kettősségét.
Skalár típusok: integer, float, bool, char
A skalár típus egyetlen értéket ír le. A Rustban négy fő csoportjuk van:
- egész számok (integer):
i8,i16,i32,i64,i128,isize(előjeles), valamintu8,u16,u32,u64,u128,usize(előjel nélküli). Alapértelmezésben a fordítói32-t választ, ha nem adsz meg típust. - lebegőpontos számok (float):
f32ésf64. Alapértelmezésf64. - logikai érték (bool):
truevagyfalse. - karakter (char): egyetlen Unicode-karakter, mindig 4 bájton tárolva — ezért a
charnem csak ASCII-t, hanem például emojit vagy ékezetes betűt is tud tárolni.
fn main() {
let age: u8 = 32;
let temperature: f64 = -3.5;
let is_rust_fun: bool = true;
let initial: char = 'R';
println!("Kor: {}, hőmérséklet: {}, szereted a Rust-ot: {}, kezdőbetű: {}", age, temperature, is_rust_fun, initial);
}
A típusok mérete korlátozott, és a fordító ezt fordítási időben ellenőrzi is, ha a literál nyilvánvalóan nem fér bele:
fn main() {
let small: u8 = 300; // u8 csak 0 és 255 közötti értéket bír el
println!("{}", small);
}
error: literal out of range for `u8`
|
= note: the literal `300` does not fit into the type `u8` whose range is `0..=255`
Ha nem vagy biztos egy típus határaiban, a rustc fordító hibaüzenete általában pontosan megmondja a megengedett tartományt — érdemes elolvasni, nem csak átugrani rajta.
Tuple: rögzített hosszú, kevert típusú csoport
A tuple több értéket fog össze egyetlen összetett típusba, és az egyes elemek típusa eltérhet egymástól. A tuple mérete fix: annyi elemet tartalmaz, amennyivel létrehoztad, se többet, se kevesebbet.
fn main() {
let point: (f64, f64, &str) = (3.5, -2.1, "A pont");
println!("Az X koordináta: {}", point.0);
println!("Az Y koordináta: {}", point.1);
println!("Címke: {}", point.2);
// az elemek egyszerre is kibonthatók:
let (x, y, label) = point;
println!("{}: ({}, {})", label, x, y);
}
Az egyes elemeket ponttal és sorszámmal éred el (point.0, point.1, ...), nullától indexelve. Ha egy tuple-t mut-tal hozol létre, az elemei módosíthatók:
fn main() {
let mut pair: (i32, i32) = (1, 2);
pair.0 = 10;
println!("{}, {}", pair.0, pair.1);
}
Array: fix méretű, egységes típusú tömb
Az array a tuple-lel ellentétben csak egyfajta típusú elemet tartalmazhat, de méretét is fixen rögzíti a típusa: [T; N], ahol T az elemtípus, N pedig az elemszám.
fn main() {
let scores: [i32; 5] = [10, 20, 30, 40, 50];
println!("Első elem: {}", scores[0]);
println!("Utolsó elem: {}", scores[4]);
println!("Az array mérete: {}", scores.len());
}
Az elemeket szögletes zárójellel és indexszel éred el, a len() pedig megadja az elemek számát. Mivel a méret a típus része, egy [i32; 5] sosem lehet 6 elemű — ezt a fordító garantálja. Ha a típusok nem egyeznek az elemek között, a fordító azonnal jelez:
fn main() {
let numbers = [1, "kettő", 3];
println!("{}", numbers[0]);
}
error[E0308]: mismatched types
|
| let numbers = [1, "kettő", 3];
| ^^^^^^^ expected integer, found `&str`
A mut kulcsszó array esetén is ugyanúgy működik, mint eddig megismerted: mut nélkül az elemek nem módosíthatók, mut-tal viszont igen.
fn main() {
let mut scores: [i32; 3] = [10, 20, 30];
scores[1] = 99;
println!("Módosított második elem: {}", scores[1]);
}
String vs &str: két szöveges típus, két szerep
A Rustban két gyakran használt szöveges típussal találkozol: a String és a &str. Elsőre zavaró lehet, hogy miért kettő is van, de a szerepük valójában eltérő.
A sztring literál (a "..." idézőjelek közé írt szöveg) mindig &str típusú: ez egy fix, csak olvasható szövegdarab, amely már valahol létezik a memóriában. A String ezzel szemben egy saját, növelhető szövegpuffer, amit futás közben bővíthetsz, módosíthatsz.
fn main() {
let greeting: &str = "Szia, Rust!"; // string literál, típusa mindig &str
let mut name: String = String::from("Anna");
name.push_str(" Kovács"); // csak String bővíthető így
println!("{}", greeting);
println!("{}", name);
println!("A név hossza: {}", name.len());
}
A &str nem bővíthető, mert nem a saját memóriáját birtokolja, csak "belenéz" egy meglévő szövegbe:
fn main() {
let text: &str = "Rust";
text.push_str(" lang"); // &str-nek nincs push_str metódusa
println!("{}", text);
}
error[E0599]: no method named `push_str` found for reference `&str` in the current scope
Előretekintés: a &str neve nem véletlenül tartalmaz egy & jelet — ez arra utal, hogy ez egy nem tulajdonló, csak rápillantó típus. A & szimbólum pontos jelentésével (referenciák, borrowing) egy későbbi leckében foglalkozunk részletesen, most elég annyit tudni, hogy a String a szöveg tulajdonosa, a &str pedig csak egy nézet rá.
Ökölszabályként: ha egy szöveget építeni, bővíteni akarsz, String-et használj; ha csak olvasod vagy átadod egy meglévő szöveget, gyakran elég a &str is.
Próbáld ki!
Hozz létre egy mut nélküli tuple-t három elemmel: egy &str névvel, egy u8 korral és egy bool értékkel (pl. hogy tanult-e már Rustot korábban). Írd ki mindhárom elemet külön println!-nel a .0, .1, .2 szintaxissal. Ezután hozz létre egy [f64; 4] array-t négy tetszőleges hőmérséklet-adattal, írd ki az elsőt és az utolsót, majd mut hozzáadásával módosítsd az array második elemét, és írd ki újra a teljes eredményt.
Önellenőrzés
1. Miért ad fordítási hibát a let small: u8 = 300; sor, miközben a 300 egy teljesen érvényes egész szám?
Megoldás
Mert a `u8` típus csak 0 és 255 közötti értékeket tud ábrázolni (8 bites, előjel nélküli). A `300` nem fér bele ebbe a tartományba, és ezt a fordító már fordítási időben észreveszi, mivel a literál értéke és a célváltozó típusa is ismert.2. Mi a fő különbség a tuple és az array között?
Megoldás
A tuple elemei eltérő típusúak lehetnek, és `.0`, `.1` stb. formában érhetők el, míg az array minden eleme azonos típusú, és indexeléssel (`tomb[0]`) érhető el. Az array típusa a méretét is tartalmazza (`[T; N]`), a tuple típusa pedig az egyes elemek típusainak sorozata (`(T1, T2, ...)`).3. Mikor válassz String-et, és mikor elég egy &str?
Megoldás
`String`-et akkor használj, ha a szöveget futás közben szeretnéd építeni, bővíteni vagy módosítani (ehhez a saját, növelhető memóriaterületre van szükség). `&str`-t akkor, ha csak egy meglévő, változatlan szövegre van szükséged — például egy string literálra, vagy amikor csak olvasod, átadod a szöveget anélkül, hogy tulajdonosává válnál.Összefoglaló
- A Rust skalár típusai (integer, float, bool, char) fordítási időben rögzített méretűek és tartományúak — a fordító már a
cargo build/cargo checksorán jelzi, ha egy literál nem fér bele egy típusba. - A tuple
(T1, T2, ...)eltérő típusú elemeket fűz össze fix hosszban, elérésük.0,.1, ... szintaxissal történik. - Az array
[T; N]azonos típusú elemeket tárol fix, a típusba kódolt méretben, indexeléssel elérve. - A
Stringa szöveg tulajdonosa és bővíthető, a&stregy nem tulajdonló, csak olvasható nézet egy szövegre — a mögöttük álló&jelentését hamarosan, az ownership-témakörnél tisztázzuk részletesen. - A
mutkulcsszó ugyanúgy vezérli a módosíthatóságot ezeknél az összetett típusoknál is, mint amit az előző leckében a skalár változóknál láttál.
A következő leckében megtanulod, hogyan irányíthatod a program futását: jön az if/else, a match, valamint a loop és társai — innentől már nem csak kiszámolni, hanem el is dönteni tudsz dolgokat a kódodban.
További olvasmányok: