اغلب مفید است که مجموعهای از عناصر را با هم گروهبندی کنید و با این گروهها بهعنوان یک واحد رفتار کنید. در Rust به چنین گروهی «ساختار» میگوییم و به هر عنصر آن، یکی از فیلدهای همان ساختار میگوییم. یک ساختار مجموعهی کلی فیلدهای در دسترس را تعریف میکند، اما به هر ساختار مشخصی که در کد ساخته میشود، یک «نمونه» از آن میگویند.
علاوه بر این، روی ساختارها میتوان متد تعریف کرد و این متدها به فیلدها دسترسی دارند. در این حالت به خود ساختار self میگویند. وقتی متدی از &mut self استفاده میکند، میتوان فیلدها را تغییر داد یا به عبارتی آنها را تغییرپذیر کرد. وقتی متدی از &self استفاده میکند، فیلدها قابل تغییر نیستند: تغییرناپذیرند. کنترل تغییرپذیری به «بررسیکنندهی قرضگیری» کمک میکند تا مطمئن شود که دستههای کاملی از باگهای همزمانی هرگز در Rust رخ نمیدهند.
در این تمرین، دو نوع متد روی یک ساختار پیادهسازی میکنید. نوع اول عموماً با نام «getter» شناخته میشود: این متدها فیلدهای ساختار را در اختیار دنیای بیرون میگذارند، بدون اینکه به کس دیگری اجازه دهند آن مقدار را تغییر دهد. در Rust، این متدها بهطور متعارف اسم همان فیلدی را دارند که آشکار میکنند؛ یعنی اگر متدی getter داشته باشیم که فیلدی با اسم name را میخواند، اسم آن متد بهسادگی name() خواهد بود.
متدهایی از نوع دیگر را هم پیادهسازی میکنید که عموماً با نام «setter» شناخته میشوند. این متدها مقدار فیلد را تغییر میدهند. متدهای setter در Rust چندان رایج نیستند: اگر فیلدی بتواند آزادانه تغییر کند، معمولاً رایجتر آن است که آن را عمومی اعلام کنند. اما اگر بهروزرسانی فیلد باید عوارض جانبی داشته باشد یا برای کنترل دسترسی به کار بیایند، مفیدند: متدی که با pub(crate) علامتگذاری شده است، به دیگر ماژولهای همان crate اجازه میدهد یک فیلد خصوصی را بهروزرسانی کنند؛ فیلدی که دنیای بیرون نمیتواند روی آن اثری بگذارد.
ساختارها سه گونه دارند: ساختارهایی با فیلدهای نامگذاریشده، ساختارهای تاپل و ساختارهای واحد. در این تمرین مفهومی، گونهی اول را بررسی میکنیم: ساختارهایی با فیلدهای نامگذاریشده.
ساختارها با کلیدواژهی struct تعریف میشوند و پس از آن، اسم نوعی که ساختار توصیف میکند با حرف بزرگ میآید:
struct Item {}
سپس انواع دیگری بهعنوان فیلدهای ساختار به بدنهی آن اضافه میشوند، هرکدام با نوع خودشان:
struct Item {
name: String,
weight: f32,
worth: u32,
}
در پایان، میتوان متدها را روی ساختار و درون یک بلوک impl تعریف کرد:
impl Item {
// initializes and returns a new instance of our Item struct
fn new() -> Self {
todo!()
}
}
حالا که این معرفی کوتاه از نحوهی نگارش ساختارها را پشت سر گذاشتیم، بروید و نگاهی به دستورالعملها این تمرین بیندازید!
در حال پیادهسازی یک سیستم پایش سلامت هستید. بهعنوان بخشی از این کار، باید آمار سلامت کاربران را پیگیری کنید.
کار را با چند تابع ناتمام در یک بلوک impl و همچنین تعریف struct زیر آغاز میکنید:
pub struct User {
name: String,
age: u32,
weight: f32,
}
هدف شما پیادهسازی متدهای ناتمام structی به اسم User است که در بلوک impl تعریف شده است.
برای مثال، متد new باید نمونهای از struct به اسم User را با اسم، سن و وزن مشخصشده برگرداند.
let mut bob = User::new(String::from("Bob"), 32, 155.2);
// Returns: a User with name "Bob", age 32, and weight 155.2
متد weight باید وزن User را برگرداند.
bob.weight();
// Returns: 155.2
متد set_age باید سن User را تنظیم کند.
bob.set_age(33);
// Updates Bob's age to 33; happy birthday Bob!
خوش بگذرانید!