
روز ۳۸: کار با Vec، HashMap و String در Rust بهصورت عملی
در Rust، ساختارهای دادهی استاندارد نقش بسیار مهمی در نوشتن برنامههای واقعی و مقیاسپذیر دارند. سه مورد از پرکاربردترین این ساختارها Vec، HashMap و String هستند که تقریباً در تمام پروژههای Rust از کوچکترین ابزارها تا بزرگترین سیستمها استفاده میشوند. برخلاف زبانهایی مثل Python یا JavaScript که مدیریت حافظه این ساختارها پنهان است، در Rust این ساختارها بهصورت شفاف با قوانین Ownership و Borrowing کار میکنند. درک درست این موضوع باعث میشود هم از نظر کارایی و هم از نظر ایمنی حافظه، کدی بسیار قدرتمند بنویسید.
Vec در Rust و مدیریت لیستهای پویا
Vec یا Vector یکی از مهمترین ساختارهای داده در Rust است که برای نگهداری مجموعهای از عناصر همنوع با اندازهی پویا استفاده میشود. Vec به شما اجازه میدهد عناصر را در زمان اجرا اضافه یا حذف کنید، بدون اینکه از قبل اندازه مشخصی داشته باشید. این ساختار داده روی heap ذخیره میشود و قوانین Ownership بهطور کامل روی آن اعمال میشود. استفادهی درست از Vec در Rust به معنای درک نحوهی انتقال مالکیت، Borrowing و Mutability است که پایهی بسیاری از الگوریتمها را تشکیل میدهد.
fn main() {
let mut numbers: Vec<i32> = Vec::new();
numbers.push(10);
numbers.push(20);
numbers.push(30);
println!("{:?}", numbers);
}در این مثال، Vec به صورت mutable تعریف شده تا بتوان عناصر جدید به آن اضافه کرد. اگر mut حذف شود، کامپایلر Rust اجازهی تغییر Vec را نخواهد داد. این رفتار باعث میشود تغییرات ناخواسته در دادهها بهراحتی شناسایی شوند.
دسترسی ایمن به عناصر Vec
یکی از ویژگیهای مهم Vec در Rust، دسترسی ایمن به عناصر است. Rust اجازه نمیدهد به صورت ناامن به ایندکسها دسترسی داشته باشید و از بروز خطاهای runtime جلوگیری میکند. شما میتوانید از indexing مستقیم یا متد get استفاده کنید که گزینهی امنتری محسوب میشود.
let v = vec![1, 2, 3];
match v.get(1) {
Some(value) => println!("{}", value),
None => println!("Index not found"),
}استفاده از get باعث میشود بهجای panic شدن برنامه، نتیجهای امن از نوع Option دریافت کنید که میتوانید آن را مدیریت کنید. این الگو در پروژههای حرفهای Rust بسیار رایج است.
String در Rust و تفاوت آن با &str
String یکی از مفاهیم چالشبرانگیز برای تازهواردها به Rust است، زیرا تفاوت مهمی با &str دارد. String یک نوع دادهی قابل تغییر است که مالک دادهی متنی خود است و روی heap ذخیره میشود، در حالی که &str یک reference به دادهی متنی است. این تفاوت نقش مهمی در مدیریت حافظه و lifetimes دارد و درک آن برای نوشتن کد حرفهای ضروری است.
let mut name = String::from("Rust");
name.push_str(" Language");
println!("{}", name);در این مثال، name یک String است و میتوان آن را تغییر داد. اگر از &str استفاده میکردیم، امکان تغییر مستقیم وجود نداشت. این تفکیک باعث میشود Rust از خطاهای رایج حافظه جلوگیری کند.
کار با UTF-8 در String
یکی از نکات مهم String در Rust این است که به صورت UTF-8 ذخیره میشود. این موضوع باعث میشود دسترسی مستقیم به کاراکترها همیشه ساده نباشد، زیرا هر کاراکتر ممکن است بیش از یک بایت باشد. Rust عمداً این محدودیت را اعمال کرده تا از بروز خطاهای متنی جلوگیری کند.