Rust مجموعهای از ساختارهای دادهی قدرتمند و ایمن را در اختیار برنامهنویس قرار میدهد که هم در زمان کامپایل بررسی میشوند و هم از نظر عملکرد بسیار سریعاند. در این بخش، با مهمترین ساختارهای داده در Rust آشنا میشویم: آرایهها، وکتورها، رشتهها، تاپلها، ساختارها، enumها، و نوعهای Option و Result.
آرایه در Rust یک مجموعه از مقادیر همنوع با طول ثابت است که در حافظه پشتسرهم ذخیره میشوند.
let numbers: [i32; 3] = [1, 2, 3];println!("{}", numbers[0]); // چاپ: 1
- نوع [i32; 3] به معنای ۳ عنصر از نوع i32 است.
- آرایهها در Rust مقداردهی پیشفرض هم دارند:
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
بر خلاف آرایهها، وکتورها قابل تغییر و دارای طول پویا هستند.
let mut vec = vec![10, 20, 30];vec.push(40); // اضافه کردن عنصرprintln!("{:?}", vec); // چاپ: [10, 20, 30, 40]
- وکتورها معمولاً با Vec شناخته میشوند.
- استفاده از vec![] ماکروی استاندارد برای ایجاد آنهاست.
Rust بین رشتههای ثابت و رشتههای قابل تغییر تفاوت قائل میشود.
&str – رشتهی ثابت:let greeting: &str = "سلام دنیا";
این نوع رشته، فقط خواندنی است و در حافظه به صورت ثابت ذخیره شده.
String – رشتهی قابل تغییر:let mut name = String::from("Ali");name.push_str(" Rezaei");println!("{}", name); // Ali Rezaei
- String در heap ذخیره میشود و میتوان آن را تغییر داد.
- تبدیل &str به String با String::from() یا .to_string() انجام میشود.
تاپل مجموعهای از مقادیر با نوعهای مختلف است.
let person: (&str, i32) = ("Ali", 30);println!("Name: {}, Age: {}", person.0, person.1);
تاپلها برای بازگرداندن چند مقدار از یک تابع بسیار مفیدند.
ساختارها امکان تعریف نوعهای دادهی سفارشی و معنادار را فراهم میکنند.
struct User {name: String,age: u8,}fn main() {let user = User {name: String::from("Sara"),age: 25,};println!("{} is {} years old", user.name, user.age);}
همچنین میتوان ساختارهای تو در تو یا مختصر (tuple structs) تعریف کرد.
با enum میتوان مقادیری تعریف کرد که میتوانند چند حالت مشخص داشته باشند:
enum Direction {North,South,East,West,}
fn move_to(dir: Direction) {match dir {Direction::North => println!("به شمال میرویم"),Direction::South => println!("به جنوب میرویم"),_ => println!("جهت دیگر"),}}
Enumها میتوانند اطلاعات همراه هم داشته باشند:
enum Message {Quit,Move { x: i32, y: i32 },Say(String),}
Rust برخلاف زبانهای دیگر از null پشتیبانی نمیکند. به جای آن از نوعهای امن استفاده میکند:
برای مقادیری که ممکن است وجود نداشته باشند.
let maybe_name: Option= Some(String::from("Ali")); let none_value: Option= None;
استفاده:
match maybe_name {Some(name) => println!("سلام {}", name),None => println!("نامی وارد نشده"),}
برای مقادیری که ممکن است با موفقیت یا خطا همراه باشند.
fn divide(x: f64, y: f64) -> Result{ if y == 0.0 {Err(String::from("تقسیم بر صفر!"))} else {Ok(x / y)}}
استفاده:
match divide(10.0, 2.0) {Ok(result) => println!("نتیجه: {}", result),Err(e) => println!("خطا: {}", e),}
در این بخش با ساختارهای دادهای مهم در Rust آشنا شدیم:
- استفاده از آرایهها برای دادههای ثابت و وکتورها برای لیستهای پویا
- تفاوت بین رشتههای خواندنی (&str) و قابل تغییر (String)
- تعریف و استفاده از تاپلها و ساختارهای سفارشی (Structs)
- طراحی حالتهای مختلف با Enums و بررسی آنها با match
- و کار با دادههای اختیاری و خطا با استفاده از Option و Result
در بخش بعدی، به سراغ ماژولها، ساختار پروژه و مدیریت کتابخانهها با Cargo میرویم.