ضمیمه ج: ویژگی‌های قابل اشتقاق

در بخش‌های مختلف کتاب، ما درباره ویژگی derive صحبت کردیم که می‌توانید آن را به تعریف یک struct یا enum اعمال کنید. ویژگی derive کدی تولید می‌کند که یک ویژگی را با پیاده‌سازی پیش‌فرض خود روی نوعی که با سینتکس derive حاشیه‌نویسی کرده‌اید، پیاده‌سازی می‌کند.

در این ضمیمه، مرجعی از تمام ویژگی‌های موجود در کتابخانه استاندارد که می‌توانید با derive استفاده کنید ارائه می‌شود. هر بخش شامل موارد زیر است:

  • چه عملگرها و متدهایی با مشتق‌سازی این ویژگی فعال می‌شوند
  • پیاده‌سازی ویژگی که توسط derive ارائه می‌شود چه می‌کند
  • پیاده‌سازی ویژگی چه مفهومی درباره نوع دارد
  • شرایطی که در آن اجازه یا عدم اجازه پیاده‌سازی ویژگی داده می‌شود
  • مثال‌هایی از عملیات‌هایی که به این ویژگی نیاز دارند

اگر رفتار متفاوتی از آنچه ویژگی derive ارائه می‌دهد می‌خواهید، به مستندات کتابخانه استاندارد برای هر ویژگی مراجعه کنید تا جزئیات مربوط به نحوه پیاده‌سازی دستی آن را بیابید.

این ویژگی‌هایی که در اینجا فهرست شده‌اند تنها ویژگی‌هایی هستند که توسط کتابخانه استاندارد تعریف شده‌اند و می‌توانند روی انواع شما با استفاده از derive پیاده‌سازی شوند. ویژگی‌های دیگر تعریف‌شده در کتابخانه استاندارد رفتار پیش‌فرض معقولی ندارند، بنابراین شما باید آن‌ها را به نحوی پیاده‌سازی کنید که با آنچه می‌خواهید انجام دهید مطابقت داشته باشد.

مثالی از یک ویژگی که نمی‌تواند مشتق شود، Display است که فرمت‌دهی برای کاربران نهایی را مدیریت می‌کند. شما باید همیشه راه مناسب برای نمایش یک نوع به کاربر نهایی را در نظر بگیرید. چه بخش‌هایی از نوع باید به کاربر نهایی نشان داده شود؟ چه بخش‌هایی برای او مرتبط است؟ چه فرمتی از داده برای او بیشترین اهمیت را دارد؟ کامپایلر Rust این بینش را ندارد، بنابراین نمی‌تواند رفتار پیش‌فرض مناسب را برای شما فراهم کند.

لیست ویژگی‌های قابل اشتقاق ارائه‌شده در این ضمیمه جامع نیست: کتابخانه‌ها می‌توانند derive را برای ویژگی‌های خود پیاده‌سازی کنند و لیست ویژگی‌هایی که می‌توانید با derive استفاده کنید را به‌طور واقعی باز بگذارند. پیاده‌سازی derive شامل استفاده از یک ماکروی فرآیندی است که در بخش “ماکروها” از فصل 20 پوشش داده شده است.

Debug برای خروجی برنامه‌نویسی

ویژگی Debug فرمت‌دهی دیباگ را در رشته‌های فرمت فعال می‌کند که با افزودن :? درون نگه‌دارنده‌های {} مشخص می‌کنید.

ویژگی Debug به شما اجازه می‌دهد نمونه‌هایی از یک نوع را برای مقاصد دیباگ چاپ کنید، به‌طوری‌که شما و سایر برنامه‌نویسانی که از نوع شما استفاده می‌کنند بتوانید نمونه‌ای را در یک نقطه خاص از اجرای برنامه بررسی کنید.

ویژگی Debug، برای مثال، در استفاده از ماکروی assert_eq! مورد نیاز است. این ماکرو مقادیر نمونه‌های داده‌شده به‌عنوان آرگومان‌ها را چاپ می‌کند اگر ادعای برابری شکست بخورد تا برنامه‌نویسان بتوانند ببینند چرا دو نمونه برابر نیستند.

PartialEq و Eq برای مقایسه برابری

ویژگی PartialEq به شما اجازه می‌دهد نمونه‌های یک نوع را برای بررسی برابری مقایسه کنید و استفاده از عملگرهای == و != را ممکن می‌سازد.

مشتق‌سازی PartialEq متد eq را پیاده‌سازی می‌کند. وقتی PartialEq روی struct‌ها مشتق می‌شود، دو نمونه فقط زمانی برابر هستند که تمام فیلدها برابر باشند و نمونه‌ها برابر نیستند اگر هر یک از فیلدها برابر نباشند. وقتی روی enum‌ها مشتق می‌شود، هر واریانت با خودش برابر است و با سایر واریانت‌ها برابر نیست.

ویژگی PartialEq، برای مثال، با استفاده از ماکروی assert_eq! مورد نیاز است که باید بتواند دو نمونه از یک نوع را برای برابری مقایسه کند.

ویژگی Eq هیچ متدی ندارد. هدف آن این است که نشان دهد برای هر مقدار از نوع حاشیه‌نویسی‌شده، مقدار با خودش برابر است. ویژگی Eq فقط می‌تواند به نوع‌هایی اعمال شود که همچنین PartialEq را پیاده‌سازی کرده باشند، اگرچه همه نوع‌هایی که PartialEq را پیاده‌سازی کرده‌اند نمی‌توانند Eq را پیاده‌سازی کنند. مثالی از این مورد نوع‌های عدد ممیز شناور هستند: پیاده‌سازی اعداد ممیز شناور بیان می‌کند که دو نمونه از مقدار غیرعدد (NaN) برابر نیستند.

مثالی از زمانی که Eq مورد نیاز است، برای کلیدها در HashMap<K, V> است تا HashMap<K, V> بتواند تعیین کند که آیا دو کلید یکسان هستند یا نه.

PartialOrd و Ord برای مقایسه مرتب‌سازی

ویژگی PartialOrd به شما امکان می‌دهد نمونه‌های یک نوع را برای اهداف مرتب‌سازی مقایسه کنید. نوعی که ویژگی PartialOrd را پیاده‌سازی می‌کند می‌تواند با عملگرهای <، >، <= و >= استفاده شود. شما فقط می‌توانید ویژگی PartialOrd را به نوع‌هایی اعمال کنید که همچنین PartialEq را پیاده‌سازی کرده باشند.

مشتق‌سازی PartialOrd متد partial_cmp را پیاده‌سازی می‌کند، که یک Option<Ordering> را برمی‌گرداند که در صورتی که مقادیر داده‌شده ترتیب‌بندی تولید نکنند، None خواهد بود. مثالی از مقداری که ترتیب‌بندی تولید نمی‌کند، حتی اگر بیشتر مقادیر آن نوع قابل مقایسه باشند، مقدار نقطه شناور غیرعدد (NaN) است. فراخوانی partial_cmp با هر عدد شناور و مقدار NaN نقطه شناور None را برمی‌گرداند.

وقتی روی struct‌ها مشتق می‌شود، PartialOrd دو نمونه را با مقایسه مقدار هر فیلد به ترتیب ظاهر شدن فیلدها در تعریف struct مقایسه می‌کند. وقتی روی enum‌ها مشتق می‌شود، واریانت‌های enum که زودتر در تعریف enum اعلام شده‌اند، کمتر از واریانت‌هایی در نظر گرفته می‌شوند که بعداً فهرست شده‌اند.

ویژگی PartialOrd، برای مثال، برای متد gen_range از crate rand مورد نیاز است که یک مقدار تصادفی در محدوده مشخص‌شده توسط یک عبارت محدوده تولید می‌کند.

ویژگی Ord به شما امکان می‌دهد بدانید که برای هر دو مقدار از نوع حاشیه‌نویسی‌شده، یک ترتیب‌بندی معتبر وجود خواهد داشت. ویژگی Ord متد cmp را پیاده‌سازی می‌کند، که به جای Option<Ordering>، یک Ordering را برمی‌گرداند زیرا یک ترتیب‌بندی معتبر همیشه ممکن خواهد بود. شما فقط می‌توانید ویژگی Ord را به نوع‌هایی اعمال کنید که همچنین PartialOrd و Eq را پیاده‌سازی کرده باشند (و Eq نیازمند PartialEq است). وقتی روی struct‌ها و enum‌ها مشتق می‌شود، cmp به همان شکلی عمل می‌کند که پیاده‌سازی مشتق‌شده برای partial_cmp در PartialOrd عمل می‌کند.

مثالی از زمانی که Ord مورد نیاز است، هنگام ذخیره مقادیر در BTreeSet<T> است، یک ساختار داده که داده‌ها را بر اساس ترتیب مرتب‌سازی مقادیر ذخیره می‌کند.

Clone و Copy برای تکثیر مقادیر

ویژگی Clone به شما امکان می‌دهد به طور صریح یک کپی عمیق از یک مقدار ایجاد کنید، و فرایند تکثیر ممکن است شامل اجرای کد دلخواه و کپی داده‌های heap باشد. برای اطلاعات بیشتر درباره Clone، به بخش “راه‌های تعامل متغیرها و داده‌ها: Clone” در فصل 4 مراجعه کنید.

مشتق‌سازی Clone متد clone را پیاده‌سازی می‌کند، که هنگام پیاده‌سازی برای کل نوع، متد clone را روی هر یک از بخش‌های نوع فراخوانی می‌کند. این بدان معناست که تمام فیلدها یا مقادیر در نوع نیز باید Clone را برای مشتق‌سازی Clone پیاده‌سازی کنند.

مثالی از زمانی که Clone مورد نیاز است، هنگام فراخوانی متد to_vec روی یک slice است. slice مالک نمونه‌های نوعی که شامل است را ندارد، اما وکتوری که از to_vec برگردانده می‌شود باید مالک نمونه‌های خود باشد، بنابراین to_vec روی هر آیتم clone را فراخوانی می‌کند. بنابراین، نوع ذخیره‌شده در slice باید Clone را پیاده‌سازی کند.

ویژگی Copy به شما امکان می‌دهد یک مقدار را با کپی کردن بیت‌های ذخیره‌شده روی stack تکثیر کنید؛ هیچ کد دلخواهی لازم نیست. برای اطلاعات بیشتر درباره Copy، به بخش “داده‌های فقط stack: Copy” در فصل 4 مراجعه کنید.

ویژگی Copy هیچ متدی را تعریف نمی‌کند تا از اضافه‌بارگذاری آن متدها توسط برنامه‌نویسان و نقض فرضی که هیچ کد دلخواهی اجرا نمی‌شود جلوگیری کند. به این ترتیب، تمام برنامه‌نویسان می‌توانند فرض کنند که کپی کردن یک مقدار بسیار سریع خواهد بود.

شما می‌توانید Copy را روی هر نوعی مشتق کنید که تمام اجزای آن Copy را پیاده‌سازی می‌کنند. نوعی که Copy را پیاده‌سازی می‌کند باید همچنین Clone را پیاده‌سازی کند، زیرا نوعی که Copy را پیاده‌سازی می‌کند دارای پیاده‌سازی ساده‌ای از Clone است که همان وظیفه را به عنوان Copy انجام می‌دهد.

ویژگی Copy به ندرت مورد نیاز است؛ نوع‌هایی که Copy را پیاده‌سازی می‌کنند بهینه‌سازی‌هایی در دسترس دارند، به این معنا که شما نیازی به فراخوانی clone ندارید، که کد را مختصرتر می‌کند.

هر چیزی که با Copy ممکن است را می‌توانید با Clone نیز انجام دهید، اما کد ممکن است کندتر باشد یا نیاز به استفاده از clone در مکان‌های مختلف داشته باشد.

Hash برای نگاشت مقدار به مقدار با اندازه ثابت

ویژگی Hash به شما امکان می‌دهد یک نمونه از نوعی با اندازه دلخواه بگیرید و آن نمونه را با استفاده از یک تابع هش به مقدار با اندازه ثابت نگاشت کنید. مشتق‌سازی Hash متد hash را پیاده‌سازی می‌کند. پیاده‌سازی مشتق‌شده متد hash نتیجه فراخوانی hash روی هر یک از بخش‌های نوع را ترکیب می‌کند، به این معنی که تمام فیلدها یا مقادیر نیز باید Hash را پیاده‌سازی کنند تا Hash مشتق شود.

مثالی از زمانی که Hash مورد نیاز است، هنگام ذخیره کلیدها در HashMap<K, V> برای ذخیره داده‌ها به صورت کارآمد است.

Default برای مقادیر پیش‌فرض

ویژگی Default به شما امکان می‌دهد یک مقدار پیش‌فرض برای یک نوع ایجاد کنید. مشتق‌سازی Default تابع default را پیاده‌سازی می‌کند. پیاده‌سازی مشتق‌شده تابع default تابع default را روی هر بخش از نوع فراخوانی می‌کند، به این معنی که تمام فیلدها یا مقادیر در نوع نیز باید Default را پیاده‌سازی کنند تا Default مشتق شود.

تابع Default::default معمولاً به همراه سینتکس به‌روزرسانی ساختار که در بخش “ایجاد نمونه‌ها از نمونه‌های دیگر با سینتکس به‌روزرسانی ساختار” در فصل 5 مورد بحث قرار گرفته است، استفاده می‌شود. می‌توانید چند فیلد از یک ساختار را سفارشی کنید و سپس یک مقدار پیش‌فرض برای بقیه فیلدها با استفاده از ..Default::default() تنظیم و استفاده کنید.

ویژگی Default، برای مثال، زمانی مورد نیاز است که از متد unwrap_or_default روی نمونه‌های Option<T> استفاده می‌کنید. اگر Option<T> برابر با None باشد، متد unwrap_or_default نتیجه Default::default را برای نوع T ذخیره‌شده در Option<T> برمی‌گرداند.