ضمیمه ج: ویژگیهای قابل اشتقاق
در بخشهای مختلف کتاب، ما درباره ویژگی 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>
برمیگرداند.