Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

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

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

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

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

traitهایی که در این‌جا فهرست شده‌اند، تنها traitهایی هستند که توسط کتابخانه استاندارد تعریف شده‌اند و می‌توان آن‌ها را با استفاده از derive روی نوع‌های خود پیاده‌سازی کرد. سایر traitهای موجود در کتابخانه استاندارد رفتاری پیش‌فرض و معنادار ندارند، بنابراین پیاده‌سازی آن‌ها به‌صورتی که با هدف شما سازگار باشد برعهده‌ی خودتان است.

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

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

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

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

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

برای مثال، trait 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 باعث پیاده‌سازی متد partial_cmp می‌شود، که یک Option<Ordering> بازمی‌گرداند؛ این مقدار در صورتی None خواهد بود که مقادیر داده‌شده نتوانند ترتیب مشخصی تولید کنند. مثالی از مقداری که ترتیب‌پذیر نیست، هرچند بیشتر مقادیر آن نوع قابل مقایسه‌اند، مقدار NaN در اعداد اعشاری (floating point) است. فراخوانی partial_cmp با هر عدد اعشاری و مقدار NaN منجر به بازگشت None می‌شود.

مشتق‌سازی 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> است، یک ساختار داده که داده‌ها را بر اساس ترتیب مرتب‌سازی مقادیر ذخیره می‌کند.

trait Clone به شما امکان می‌دهد که به‌صورت صریح یک کپی عمیق از یک مقدار ایجاد کنید، و این فرایند تکثیر ممکن است شامل اجرای کد دلخواه و کپی‌کردن داده‌ها از حافظه heap باشد. برای اطلاعات بیشتر درباره‌ی Clone به بخش «متغیرها و داده‌ها در تعامل با Clone» در فصل ۴ مراجعه کنید.

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

trait Copy به شما اجازه می‌دهد که یک مقدار را تنها با کپی‌کردن بیت‌های ذخیره‌شده در stack تکثیر کنید؛ هیچ کد دلخواهی اجرا نمی‌شود. برای اطلاعات بیشتر درباره‌ی Copy به بخش «داده‌های فقط-روی-استک: Copy» در فصل ۴ مراجعه کنید.

ویژگی 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 معمولاً همراه با نگارش به‌روزرسانی ساختار (struct update syntax) که در بخش «ایجاد نمونه‌هایی از نمونه‌های دیگر با استفاده از نگارش به‌روزرسانی ساختار» در فصل ۵ توضیح داده شده، استفاده می‌شود. می‌توانید تنها چند فیلد از یک ساختار را شخصی‌سازی کنید و سپس برای فیلدهای باقی‌مانده از مقدار پیش‌فرض با استفاده از ..Default::default() بهره ببرید.

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

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