পরিশিষ্ট গ: ডিরাইভেবল ট্রেইট
বইয়ের বিভিন্ন জায়গায় আমরা derive
অ্যাট্রিবিউট নিয়ে আলোচনা করেছি, যা আপনি একটি struct
বা enum
ডেফিনিশনে প্রয়োগ করতে পারেন। derive
অ্যাট্রিবিউট এমন কোড জেনারেট করে যা আপনার derive
সিনট্যাক্স দিয়ে অ্যানোটেট করা টাইপের উপর একটি ট্রেইটের ডিফল্ট ইমপ্লিমেন্টেশন তৈরি করে।
এই পরিশিষ্টে, আমরা স্ট্যান্ডার্ড লাইব্রেরির সেই সমস্ত ট্রেইটের একটি রেফারেন্স প্রদান করছি যা আপনি derive
এর সাথে ব্যবহার করতে পারেন। প্রতিটি বিভাগে আলোচনা করা হয়েছে:
- এই ট্রেইট ডিরাইভ করলে কোন অপারেটর এবং মেথডগুলো ব্যবহার করা যাবে
derive
দ্বারা প্রদত্ত ট্রেইটের ইমপ্লিমেন্টেশন কী করে- ট্রেইট ইমপ্লিমেন্ট করা টাইপটি সম্পর্কে কী বোঝায়
- কোন শর্তে আপনি ট্রেইটটি ইমপ্লিমেন্ট করতে পারবেন বা পারবেন না
- যেসব অপারেশনের জন্য এই ট্রেইট প্রয়োজন তার উদাহরণ
আপনি যদি derive
অ্যাট্রিবিউট দ্বারা প্রদত্ত আচরণের থেকে ভিন্ন আচরণ চান, তবে কীভাবে সেগুলো ম্যানুয়ালি ইমপ্লিমেন্ট করতে হয় তার বিস্তারিত জানার জন্য প্রতিটি ট্রেইটের জন্য স্ট্যান্ডার্ড লাইব্রেরি ডকুমেন্টেশন দেখুন।
এখানে তালিকাভুক্ত ট্রেইটগুলোই স্ট্যান্ডার্ড লাইব্রেরি দ্বারা সংজ্ঞায়িত একমাত্র ট্রেইট যা derive
ব্যবহার করে আপনার টাইপের উপর ইমপ্লিমেন্ট করা যেতে পারে। স্ট্যান্ডার্ড লাইব্রেরিতে সংজ্ঞায়িত অন্যান্য ট্রেইটগুলোর কোনো সংবেদনশীল ডিফল্ট আচরণ নেই, তাই আপনি কী অর্জন করার চেষ্টা করছেন তার উপর ভিত্তি করে সেগুলোকে আপনার নিজের মতো করে ইমপ্লিমেন্ট করতে হবে।
এমন একটি ট্রেইটের উদাহরণ যা ডিরাইভ করা যায় না তা হলো Display
, যা এন্ড-ইউজারদের জন্য ফরম্যাটিং পরিচালনা করে। আপনার সবসময় একজন এন্ড-ইউজারের কাছে একটি টাইপ প্রদর্শনের সবচেয়ে উপযুক্ত উপায় কী তা বিবেচনা করা উচিত। টাইপের কোন অংশগুলো একজন এন্ড-ইউজার দেখতে পাবে? কোন অংশগুলো তাদের কাছে প্রাসঙ্গিক মনে হবে? ডেটার কোন ফরম্যাটটি তাদের জন্য সবচেয়ে প্রাসঙ্গিক হবে? Rust কম্পাইলারের এই অন্তর্দৃষ্টি নেই, তাই এটি আপনার জন্য উপযুক্ত ডিফল্ট আচরণ প্রদান করতে পারে না।
এই পরিশিষ্টে দেওয়া ডিরাইভেবল ট্রেইটের তালিকাটি সম্পূর্ণ নয়: লাইব্রেরিগুলো তাদের নিজস্ব ট্রেইটের জন্য derive
ইমপ্লিমেন্ট করতে পারে, যার ফলে আপনি derive
ব্যবহার করতে পারেন এমন ট্রেইটের তালিকাটি সত্যিই অফুরন্ত। derive
ইমপ্লিমেন্ট করার জন্য একটি প্রসিডিউরাল ম্যাক্রো ব্যবহার করতে হয়, যা অধ্যায় ২০-এর “ম্যাক্রো” বিভাগে আলোচনা করা হয়েছে।
প্রোগ্রামার আউটপুটের জন্য 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
) ফ্লোটিং পয়েন্ট ভ্যালু। যেকোনো ফ্লোটিং-পয়েন্ট নাম্বার এবং NaN
ফ্লোটিং-পয়েন্ট ভ্যালু দিয়ে partial_cmp
কল করলে None
রিটার্ন হবে।
struct-এর উপর ডিরাইভ করা হলে, PartialOrd
struct ডেফিনিশনে ফিল্ডগুলো যে ক্রমে উপস্থিত হয় সেই ক্রমে প্রতিটি ফিল্ডের ভ্যালু তুলনা করে দুটি ইনস্ট্যান্স তুলনা করে। enum-এর উপর ডিরাইভ করা হলে, enum ডেফিনিশনে আগে ঘোষিত enum-এর ভ্যারিয়েন্টগুলো পরে তালিকাভুক্ত ভ্যারিয়েন্টগুলোর চেয়ে ছোট বলে বিবেচিত হয়।
PartialOrd
ট্রেইটটি প্রয়োজন হয়, উদাহরণস্বরূপ, rand
ক্রেটের gen_range
মেথডের জন্য যা একটি রেঞ্জ এক্সপ্রেশন দ্বারা নির্দিষ্ট করা রেঞ্জের মধ্যে একটি র্যান্ডম ভ্যালু তৈরি করে।
Ord
ট্রেইট আপনাকে জানতে দেয় যে অ্যানোটেটেড টাইপের যেকোনো দুটি ভ্যালুর জন্য, একটি বৈধ ক্রম বিদ্যমান থাকবে। Ord
ট্রেইট cmp
মেথড ইমপ্লিমেন্ট করে, যা একটি Ordering
রিটার্ন করে, Option<Ordering>
নয়, কারণ একটি বৈধ ক্রম সর্বদা সম্ভব হবে। আপনি Ord
ট্রেইট শুধুমাত্র সেই টাইপগুলোতে প্রয়োগ করতে পারেন যেগুলো PartialOrd
এবং Eq
(এবং Eq
-এর জন্য PartialEq
প্রয়োজন) উভয়ই ইমপ্লিমেন্ট করে। struct এবং enum-এর উপর ডিরাইভ করা হলে, cmp
PartialOrd
-এর সাথে partial_cmp
-এর ডিরাইভড ইমপ্লিমেন্টেশনের মতোই আচরণ করে।
Ord
কখন প্রয়োজন তার একটি উদাহরণ হলো BTreeSet<T>
-এ ভ্যালু সংরক্ষণ করার সময়, যা একটি ডেটা স্ট্রাকচার যা ভ্যালুগুলোর সর্ট অর্ডারের উপর ভিত্তি করে ডেটা সংরক্ষণ করে।
ভ্যালু ডুপ্লিকেট করার জন্য Clone
এবং Copy
Clone
ট্রেইট আপনাকে একটি ভ্যালুর একটি ডিপ কপি স্পষ্টভাবে তৈরি করার অনুমতি দেয়, এবং এই ডুপ্লিকেশন প্রক্রিয়ায় আর্বিট্রারি কোড চালানো এবং হিপ ডেটা কপি করা জড়িত থাকতে পারে। Clone
সম্পর্কে আরও তথ্যের জন্য অধ্যায় ৪-এর “ভেরিয়েবল এবং ডেটার সাথে ক্লোনের ইন্টারঅ্যাকশন” দেখুন।
Clone
ডিরাইভ করলে clone
মেথড ইমপ্লিমেন্ট হয়, যা পুরো টাইপের জন্য ইমপ্লিমেন্ট করা হলে, টাইপের প্রতিটি অংশের উপর clone
কল করে। এর মানে হলো Clone
ডিরাইভ করার জন্য টাইপের সমস্ত ফিল্ড বা ভ্যালুগুলোকেও অবশ্যই Clone
ইমপ্লিমেন্ট করতে হবে।
Clone
কখন প্রয়োজন তার একটি উদাহরণ হলো একটি স্লাইসের উপর to_vec
মেথড কল করার সময়। স্লাইসটি তার মধ্যে থাকা টাইপ ইনস্ট্যান্সগুলোর মালিক নয়, কিন্তু to_vec
থেকে রিটার্ন করা ভেক্টরকে তার ইনস্ট্যান্সগুলোর মালিক হতে হবে, তাই to_vec
প্রতিটি আইটেমের উপর clone
কল করে। সুতরাং স্লাইসে সংরক্ষিত টাইপটিকে অবশ্যই Clone
ইমপ্লিমেন্ট করতে হবে।
Copy
ট্রেইট আপনাকে শুধুমাত্র স্ট্যাকে সংরক্ষিত বিট কপি করে একটি ভ্যালু ডুপ্লিকেট করার অনুমতি দেয়; কোনো আর্বিট্রারি কোডের প্রয়োজন নেই। Copy
সম্পর্কে আরও তথ্যের জন্য অধ্যায় ৪-এর “শুধুমাত্র-স্ট্যাক ডেটা: কপি” দেখুন।
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
ফাংশনটি সাধারণত স্ট্রাকট আপডেট সিনট্যাক্সের সাথে একত্রে ব্যবহৃত হয় যা অধ্যায় ৫-এর “স্ট্রাকট আপডেট সিনট্যাক্স দিয়ে অন্যান্য ইনস্ট্যান্স থেকে ইনস্ট্যান্স তৈরি করা” এ আলোচনা করা হয়েছে। আপনি একটি struct-এর কয়েকটি ফিল্ড কাস্টমাইজ করতে পারেন এবং তারপর ..Default::default()
ব্যবহার করে বাকি ফিল্ডগুলোর জন্য একটি ডিফল্ট ভ্যালু সেট এবং ব্যবহার করতে পারেন।
Default
ট্রেইটটি প্রয়োজন হয় যখন আপনি Option<T>
ইনস্ট্যান্সের উপর unwrap_or_default
মেথড ব্যবহার করেন, উদাহরণস্বরূপ। যদি Option<T>
টি None
হয়, তবে unwrap_or_default
মেথডটি Option<T>
-এ সংরক্ষিত T
টাইপের জন্য Default::default
-এর ফলাফল রিটার্ন করবে।