اغلب مفید است که مجموعهای از عنصرها را کنار هم گروهبندی کنیم و با این گروهها بهعنوان واحدهایی یکپارچه برخورد کنیم.
در Cairo، به چنین گروهی struct میگوییم و به هر عنصر، یکی از «فیلد»های آن struct.
یک struct مجموعهی کلی فیلدهای موجود را تعریف میکند، اما به یک نمونهی مشخص از آن، «نمونه» میگوییم.
علاوه بر این، میتوان روی structها «متد»هایی تعریف کرد که به فیلدها دسترسی دارند.
در این حالت، خودِ struct با نام self شناخته میشود.
وقتی یک متد از ref self: SomeStruct استفاده میکند، فیلدها قابل تغییرند، یعنی میتوان آنها را تغییر داد.
وقتی یک متد از self: SomeStruct یا self: @SomeStruct استفاده میکند، فیلدها قابل تغییر نیستند: آنها تغییرناپذیرند.
کنترل تغییرپذیری به borrow-checker کمک میکند تا مطمئن شود که کلاسهای کاملی از باگهای همروندی در Cairo اصلاً رخ نمیدهند.
در این تمرین، دو نوع متد را روی یک struct پیادهسازی میکنید.
نوع اول که معمولاً «گیرنده» نامیده میشود، فیلدهای struct را در دسترس دیگران قرار میدهد، بدون اینکه بگذارد کس دیگری آن مقدار را تغییر دهد.
همچنین متدهایی از نوع دیگر را پیادهسازی میکنید که معمولاً «تنظیمکننده» نامیده میشوند. اینها مقدار فیلد را تغییر میدهند. تنظیمکنندهها در Cairo چندان رایج نیستند؛ اگر بتوان فیلدی را آزادانه تغییر داد، معمولاً آن را عمومی میکنیم. اما اگر بهروزرسانی فیلد باید عوارض جانبی داشته باشد، این متدها مفیدند.
structها با «کلیدواژه»ی struct تعریف میشوند و پس از آن، نام نوعی میآید که این struct توصیف میکند، با حرف اول بزرگ:
struct Item {}
سپس انواع دیگری بهعنوان فیلدهای struct به بدنهی آن آورده میشوند، هرکدام با نوع خودشان:
struct Item {
name: String,
weight: f32,
worth: u32,
}
یک trait مجموعهای از متدها را تعریف میکند که یک نوع میتواند پیادهسازیشان کند (اینجا روی structها تمرکز میکنیم، اما traitها را میتوان روی enumها هم پیادهسازی کرد).
وقتی این trait پیادهسازی شد، میتوان این متدها را روی نمونههای آن نوع فراخوانی کرد.
traitها با کلیدواژهی trait تعریف میشوند و درون آنها امضای متدهایی را تعریف میکنیم که میخواهیم نوعمان پیادهسازی کند.
trait ImplTrait {
// Define the method signature
fn new() -> Item;
}
در پایان، میتوان متدها را روی structها درون یک «بلوک» impl تعریف کرد که trait تعریفشده را پیادهسازی میکند:
impl ItemImpl of ImplTrait {
// initializes and returns a new instance of our Item struct
fn new() -> Item {
Item {}
}
}
شما در حال پیادهسازی یک سیستم پایش سلامت هستید. به عنوان بخشی از آن، باید آمار سلامت کاربران را پیگیری کنید.
شما کار خود را با چند تابع ناتمام در یک بلوک impl و همچنین تعریف struct زیر آغاز میکنید:
pub struct User {
name: String,
age: u32,
weight: u32,
}
هدف شما این است که متدهای ناتمام را روی structی به اسم User که در بلوک impl تعریف شده است پیادهسازی کنید.
برای مثال، متد new باید نمونهای از struct User را با مقادیر مشخصشده برای اسم، سن و وزن برگرداند.
let mut bob = User::new("Bob", 32, 72);
// Returns: a User with name "Bob", age 32, and weight 72
متد weight باید وزن User را برگرداند.
bob.weight();
// Returns: 72
متد set_age باید سن User را تنظیم کند.
bob.set_age(33);
// Updates Bob's age to 33; happy birthday Bob!
خوش بگذرانید!