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 অ্যাট্রিবিউট দ্বারা প্রদত্ত আচরণের থেকে ভিন্ন আচরণ চান, তবে কীভাবে সেগুলো ম্যানুয়ালি ইমপ্লিমেন্ট করতে হয় তার বিস্তারিত জানার জন্য প্রতিটি ট্রেইটের জন্য স্ট্যান্ডার্ড লাইব্রেরি ডকুমেন্টেশন দেখুন।

এখানে তালিকাভুক্ত ট্রেইটগুলোই স্ট্যান্ডার্ড লাইব্রেরি দ্বারা সংজ্ঞায়িত একমাত্র ট্রেইট যা 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-এর ফলাফল রিটার্ন করবে।