Rust

روز ۳۸: کار با 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 عمداً این محدودیت را اعمال کرده تا از بروز خطاهای متنی جلوگیری کند.

let text = String::from("سلام");
for c in text.chars() {
    println!("{}", c);
}

در این مثال، استفاده از chars باعث می‌شود به صورت ایمن روی کاراکترهای یونیکد پیمایش انجام شود. این رفتار در برنامه‌های چندزبانه بسیار مهم است.

HashMap در Rust و ذخیره داده‌های کلید-مقدار

HashMap یکی دیگر از ساختارهای داده‌ی بسیار پرکاربرد در Rust است که برای ذخیره‌ی داده‌ها به صورت کلید-مقدار استفاده می‌شود. HashMap به شما اجازه می‌دهد داده‌ها را سریع جستجو کنید و برای پیاده‌سازی Cache، تنظیمات، شمارنده‌ها و بسیاری از سناریوهای دیگر ایده‌آل است. مانند سایر ساختارها، HashMap نیز قوانین Ownership را رعایت می‌کند.

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Alice", 10);
    scores.insert("Bob", 20);
    println!("{:?}", scores);
}

در این مثال، کلیدها و مقادیر مالکیت داده را به HashMap منتقل می‌کنند. این موضوع باعث می‌شود مدیریت حافظه شفاف و ایمن باشد.

به‌روزرسانی مقادیر در HashMap

Rust ابزارهای قدرتمندی برای کار با HashMap فراهم کرده است، از جمله متد entry که امکان درج یا به‌روزرسانی داده را به‌صورت امن فراهم می‌کند. این متد یکی از نقاط قوت طراحی API در Rust محسوب می‌شود.

scores.entry("Alice").and_modify(|v| *v += 5).or_insert(5);

این خط کد نشان می‌دهد که اگر کلید وجود داشته باشد مقدار آن تغییر می‌کند و اگر وجود نداشته باشد مقدار جدید درج می‌شود. چنین الگوهایی کد را کوتاه، خوانا و ایمن می‌کنند.

ترکیب Vec، String و HashMap در پروژه واقعی

در پروژه‌های واقعی، معمولاً این سه ساختار داده در کنار هم استفاده می‌شوند. برای مثال، می‌توان لیستی از کاربران را با Vec نگه‌داری کرد، نام آن‌ها را با String ذخیره کرد و اطلاعات اضافی را در HashMap قرار داد. Rust با ترکیب این ساختارها و قوانین Ownership، برنامه‌هایی بسیار پایدار و سریع ایجاد می‌کند.

struct User {
    name: String,
    attributes: HashMap<String, String>,
}

این Struct نمونه‌ای از استفاده‌ی هم‌زمان Vec، String و HashMap در طراحی سیستم‌های واقعی است و در بسیاری از پروژه‌ها الگوی رایجی محسوب می‌شود.

جمع‌بندی نهایی

Vec، String و HashMap ستون‌های اصلی کار با داده در Rust هستند و بدون تسلط بر آن‌ها، نوشتن برنامه‌های واقعی تقریباً غیرممکن است. این ساختارها در کنار قوانین Ownership و Borrowing باعث می‌شوند Rust هم ایمن باشد و هم سریع. تمرین عملی با این ساختارها به شما کمک می‌کند ذهنیت درستی نسبت به مدیریت حافظه پیدا کنید و کدی حرفه‌ای و قابل نگهداری بنویسید. تسلط بر این مفاهیم شما را برای ورود به مباحث پیشرفته‌تر مانند Concurrency و Async Rust کاملاً آماده می‌کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

هفده + 14 =

همچنین ببینید
بستن
دکمه بازگشت به بالا