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

একটি Enum ডিফাইন করা

যেখানে struct আপনাকে সম্পর্কিত field এবং data-কে একসাথে গ্রুপ করার একটি উপায় দেয়, যেমন একটি Rectangle এর width এবং height, সেখানে enum আপনাকে বলার একটি উপায় দেয় যে একটি value একটি সম্ভাব্য সেটের মধ্যে একটি। উদাহরণস্বরূপ, আমরা বলতে পারি যে Rectangle একটি সম্ভাব্য shape-এর সেট-এর মধ্যে একটি, যার মধ্যে Circle এবং Triangle-ও রয়েছে। এটি করার জন্য, Rust আমাদের এই সম্ভাবনাগুলোকে একটি enum হিসাবে এনকোড করার অনুমতি দেয়।

আসুন আমরা এমন একটি পরিস্থিতি দেখি যা আমরা কোডে প্রকাশ করতে চাই এবং দেখি কেন এই ক্ষেত্রে struct-এর চেয়ে enum বেশি উপযোগী এবং উপযুক্ত। ধরুন আমাদের IP address নিয়ে কাজ করতে হবে। বর্তমানে, IP address-এর জন্য দুটি প্রধান standard ব্যবহৃত হয়: ভার্সন ফোর এবং ভার্সন সিক্স। যেহেতু আমাদের প্রোগ্রামে একটি IP address-এর জন্য এই দুটিই একমাত্র সম্ভাবনা, তাই আমরা সমস্ত সম্ভাব্য variant-গুলোকে enumerate বা গণনা করতে পারি, যেখান থেকে enumeration নামটি এসেছে।

যেকোনো IP address হয় ভার্সন ফোর বা ভার্সন সিক্স হতে পারে, কিন্তু একই সাথে উভয়ই হতে পারে না। IP address-এর এই বৈশিষ্ট্যটি enum ডেটা স্ট্রাকচারকে উপযুক্ত করে তোলে কারণ একটি enum value তার variant-গুলোর মধ্যে শুধুমাত্র একটি হতে পারে। ভার্সন ফোর এবং ভার্সন সিক্স উভয় address-ই এখনও মৌলিকভাবে IP address, তাই যখন কোডটি যেকোনো ধরনের IP address-এর জন্য প্রযোজ্য পরিস্থিতি পরিচালনা করে, তখন তাদের একই type হিসাবে গণ্য করা উচিত।

আমরা এই ধারণাটি কোডে প্রকাশ করতে পারি একটি IpAddrKind enum ডিফাইন করে এবং একটি IP address-এর সম্ভাব্য প্রকার V4 এবং V6-কে তালিকাভুক্ত করে। এগুলি হলো enum-এর variant:

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

IpAddrKind এখন একটি কাস্টম ডেটা type যা আমরা আমাদের কোডের অন্য কোথাও ব্যবহার করতে পারি।

Enum ভ্যালু

আমরা IpAddrKind-এর দুটি variant-এর প্রত্যেকটির instance এভাবে তৈরি করতে পারি:

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

লক্ষ্য করুন যে enum-এর variant-গুলো তার আইডেন্টিফায়ারের অধীনে namespaced থাকে এবং আমরা দুটিকে আলাদা করতে একটি ডাবল কোলন (::) ব্যবহার করি। এটি দরকারী কারণ এখন IpAddrKind::V4 এবং IpAddrKind::V6 উভয়ই একই type-এর: IpAddrKind। এর ফলে আমরা, উদাহরণস্বরূপ, একটি function ডিফাইন করতে পারি যা যেকোনো IpAddrKind গ্রহণ করে:

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

এবং আমরা এই function-টিকে যেকোনো variant দিয়ে কল করতে পারি:

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

enum ব্যবহার করার আরও সুবিধা আছে। আমাদের IP address type সম্পর্কে আরও ভাবলে দেখা যায়, এই মুহূর্তে আমাদের কাছে আসল IP address data সংরক্ষণ করার কোনো উপায় নেই; আমরা কেবল জানি এটি কোন kind বা প্রকারের। চ্যাপ্টার ৫-এ আপনি যেহেতু struct সম্পর্কে শিখেছেন, আপনি হয়তো এই সমস্যাটি struct দিয়ে সমাধান করার চেষ্টা করতে পারেন, যেমনটি লিস্টিং ৬-১ এ দেখানো হয়েছে।

fn main() {
    enum IpAddrKind {
        V4,
        V6,
    }

    struct IpAddr {
        kind: IpAddrKind,
        address: String,
    }

    let home = IpAddr {
        kind: IpAddrKind::V4,
        address: String::from("127.0.0.1"),
    };

    let loopback = IpAddr {
        kind: IpAddrKind::V6,
        address: String::from("::1"),
    };
}

এখানে, আমরা একটি IpAddr struct ডিফাইন করেছি যার দুটি field আছে: একটি kind field যা IpAddrKind type-এর (আমাদের আগে ডিফাইন করা enum) এবং একটি address field যা String type-এর। আমাদের এই struct-এর দুটি instance আছে। প্রথমটি হলো home, এবং এটির kind হিসেবে IpAddrKind::V4 এবং এর সাথে সম্পর্কিত address data হিসেবে 127.0.0.1 আছে। দ্বিতীয় instance হলো loopback। এটির kind হিসেবে IpAddrKind-এর অন্য variant, V6 আছে এবং এর সাথে ::1 address সম্পর্কিত আছে। আমরা kind এবং address value-গুলোকে একসাথে বান্ডিল করতে একটি struct ব্যবহার করেছি, তাই এখন variant-টি value-এর সাথে সম্পর্কিত।

যাইহোক, শুধু একটি enum ব্যবহার করে একই ধারণা প্রকাশ করা আরও সংক্ষিপ্ত: একটি struct-এর ভিতরে enum না রেখে, আমরা সরাসরি প্রতিটি enum variant-এর মধ্যে data রাখতে পারি। IpAddr enum-এর এই নতুন সংজ্ঞাটি বলছে যে V4 এবং V6 উভয় variant-এর সাথেই String value যুক্ত থাকবে:

fn main() {
    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));
}

আমরা সরাসরি enum-এর প্রতিটি variant-এর সাথে data সংযুক্ত করি, তাই অতিরিক্ত struct-এর কোনো প্রয়োজন নেই। এখানে, enum কিভাবে কাজ করে তার আরেকটি বিস্তারিত দিক দেখাও সহজ: আমরা যে প্রতিটি enum variant ডিফাইন করি তার নামও একটি function হয়ে যায় যা enum-এর একটি instance তৈরি করে। অর্থাৎ, IpAddr::V4() একটি function কল যা একটি String আর্গুমেন্ট নেয় এবং IpAddr type-এর একটি instance রিটার্ন করে। enum ডিফাইন করার ফলে আমরা স্বয়ংক্রিয়ভাবে এই constructor function-টি পেয়ে যাই।

struct-এর চেয়ে enum ব্যবহার করার আরেকটি সুবিধা হলো: প্রতিটি variant-এর সাথে বিভিন্ন type এবং পরিমাণের data যুক্ত থাকতে পারে। ভার্সন ফোর IP address-এ সবসময় চারটি সাংখ্যিক কম্পোনেন্ট থাকবে যার মান ০ থেকে ২৫৫ এর মধ্যে হবে। যদি আমরা V4 address-কে চারটি u8 মান হিসেবে সংরক্ষণ করতে চাই কিন্তু V6 address-কে একটি String মান হিসাবেই প্রকাশ করতে চাই, তবে আমরা একটি struct দিয়ে তা করতে পারতাম না। enum এই কাজটি সহজেই করতে পারে:

fn main() {
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));
}

আমরা ভার্সন ফোর এবং ভার্সন সিক্স IP address সংরক্ষণ করার জন্য ডেটা স্ট্রাকচার ডিফাইন করার বিভিন্ন উপায় দেখিয়েছি। তবে, দেখা যাচ্ছে যে IP address সংরক্ষণ করা এবং সেগুলি কোন প্রকারের তা এনকোড করার প্রয়োজনীয়তা এতটাই সাধারণ যে স্ট্যান্ডার্ড লাইব্রেরিতে আমাদের ব্যবহারের জন্য একটি সংজ্ঞা রয়েছে! আসুন দেখি স্ট্যান্ডার্ড লাইব্রেরি কিভাবে IpAddr ডিফাইন করে: এটিতে আমাদের ডিফাইন করা এবং ব্যবহৃত enum এবং variant-গুলোই আছে, তবে এটি variant-গুলোর ভিতরে address data দুটি ভিন্ন struct-এর আকারে এম্বেড করে, যা প্রতিটি variant-এর জন্য ভিন্নভাবে ডিফাইন করা হয়েছে:

#![allow(unused)]
fn main() {
struct Ipv4Addr {
    // --snip--
}

struct Ipv6Addr {
    // --snip--
}

enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}
}

এই কোডটি দেখায় যে আপনি একটি enum variant-এর ভিতরে যেকোনো ধরনের ডেটা রাখতে পারেন: উদাহরণস্বরূপ, স্ট্রিং, নিউমেরিক টাইপ, বা struct। এমনকি আপনি অন্য একটি enum-ও অন্তর্ভুক্ত করতে পারেন! এছাড়াও, স্ট্যান্ডার্ড লাইব্রেরির type-গুলো প্রায়শই আপনার নিজের ভাবনার চেয়ে খুব বেশি জটিল হয় না।

লক্ষ্য করুন যে যদিও স্ট্যান্ডার্ড লাইব্রেরিতে IpAddr-এর একটি সংজ্ঞা রয়েছে, আমরা এখনও কোনো conflict ছাড়াই আমাদের নিজস্ব সংজ্ঞা তৈরি এবং ব্যবহার করতে পারি কারণ আমরা স্ট্যান্ডার্ড লাইব্রেরির সংজ্ঞাটি আমাদের স্কোপে নিয়ে আসিনি। আমরা চ্যাপ্টার ৭-এ স্কোপে type নিয়ে আসার বিষয়ে আরও আলোচনা করব।

আসুন লিস্টিং ৬-২-এ enum-এর আরেকটি উদাহরণ দেখি: এটিতে এর variant-গুলোর মধ্যে বিভিন্ন ধরণের type এম্বেড করা আছে।

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn main() {}

এই enum-টির চারটি variant আছে এবং তাদের সাথে বিভিন্ন ধরনের ডেটা যুক্ত আছে:

  • Quit: এর সাথে কোনো ডেটা যুক্ত নেই।
  • Move: এর মধ্যে struct-এর মতো named field আছে।
  • Write: একটি String অন্তর্ভুক্ত করে।
  • ChangeColor: তিনটি i32 মান অন্তর্ভুক্ত করে।

লিস্টিং ৬-২-এর মতো variant সহ একটি enum ডিফাইন করা বিভিন্ন ধরণের struct ডিফাইন করার মতোই, তবে enum struct কীওয়ার্ড ব্যবহার করে না এবং সমস্ত variant Message type-এর অধীনে একসাথে গ্রুপ করা হয়। নিম্নলিখিত struct-গুলো পূর্ববর্তী enum variant-গুলোর মতো একই ডেটা ধারণ করতে পারত:

struct QuitMessage; // unit struct
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

fn main() {}

কিন্তু যদি আমরা বিভিন্ন struct ব্যবহার করতাম, যার প্রত্যেকটির নিজস্ব type আছে, আমরা তত সহজে একটি function ডিফাইন করতে পারতাম না যা এই ধরনের যেকোনো message নিতে পারে, যেমনটা আমরা লিস্টিং ৬-২-এ ডিফাইন করা Message enum দিয়ে করতে পারি, যা একটি একক type।

enum এবং struct-এর মধ্যে আরও একটি মিল আছে: ঠিক যেমন আমরা impl ব্যবহার করে struct-এর উপর মেথড ডিফাইন করতে পারি, তেমনি আমরা enum-এর উপরও মেথড ডিফাইন করতে পারি। এখানে call নামে একটি মেথড রয়েছে যা আমরা আমাদের Message enum-এ ডিফাইন করতে পারি:

fn main() {
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }

    impl Message {
        fn call(&self) {
            // method body would be defined here
        }
    }

    let m = Message::Write(String::from("hello"));
    m.call();
}

মেথডটির বডি self ব্যবহার করে সেই মানটি পাবে যার উপর আমরা মেথডটি কল করেছি। এই উদাহরণে, আমরা একটি ভেরিয়েবল m তৈরি করেছি যার মান Message::Write(String::from("hello")), এবং যখন m.call() চালানো হবে, তখন call মেথডের বডিতে self এটিই হবে।

আসুন স্ট্যান্ডার্ড লাইব্রেরির আরেকটি enum দেখি যা খুব সাধারণ এবং দরকারী: Option

Option Enum এবং Null ভ্যালুর উপর এর সুবিধা

এই বিভাগটি Option-এর একটি কেস স্টাডি নিয়ে আলোচনা করে, যা স্ট্যান্ডার্ড লাইব্রেরি দ্বারা ডিফাইন করা আরেকটি enum। Option type-টি একটি খুব সাধারণ পরিস্থিতি এনকোড করে যেখানে একটি মান কিছু একটা হতে পারে বা কিছুই নাও হতে পারে।

উদাহরণস্বরূপ, যদি আপনি একটি খালি নয় এমন লিস্ট থেকে প্রথম আইটেমটি অনুরোধ করেন, আপনি একটি মান পাবেন। যদি আপনি একটি খালি লিস্ট থেকে প্রথম আইটেমটি অনুরোধ করেন, আপনি কিছুই পাবেন না। এই ধারণাটিকে type সিস্টেমের ভাষায় প্রকাশ করার অর্থ হলো compiler পরীক্ষা করতে পারে যে আপনি সমস্ত প্রয়োজনীয় কেসগুলি হ্যান্ডেল করেছেন কিনা; এই কার্যকারিতাটি অন্যান্য প্রোগ্রামিং ভাষায় অত্যন্ত সাধারণ বাগ প্রতিরোধ করতে পারে।

প্রোগ্রামিং ভাষার ডিজাইন প্রায়শই কোন বৈশিষ্ট্যগুলি অন্তর্ভুক্ত করা হয় তার উপর ভিত্তি করে ভাবা হয়, তবে কোন বৈশিষ্ট্যগুলি বাদ দেওয়া হয় তাও গুরুত্বপূর্ণ। Rust-এ null বৈশিষ্ট্যটি নেই যা অনেক অন্যান্য ভাষায় রয়েছে। Null হলো এমন একটি মান যার অর্থ সেখানে কোনো মান নেই। যেসব ভাষায় null আছে, সেখানে ভেরিয়েবল সবসময় দুটি অবস্থার একটিতে থাকতে পারে: null অথবা not-null

তার ২০০৯ সালের উপস্থাপনা "Null References: The Billion Dollar Mistake"-এ, null-এর উদ্ভাবক টনি হোর একথা বলেছিলেন:

আমি এটাকে আমার বিলিয়ন-ডলারের ভুল বলি। সেই সময়ে, আমি একটি অবজেক্ট-ওরিয়েন্টেড ভাষার জন্য রেফারেন্সের প্রথম ব্যাপক টাইপ সিস্টেম ডিজাইন করছিলাম। আমার লক্ষ্য ছিল নিশ্চিত করা যে রেফারেন্সের সমস্ত ব্যবহার একেবারে নিরাপদ হবে, এবং compiler দ্বারা স্বয়ংক্রিয়ভাবে পরীক্ষা করা হবে। কিন্তু আমি একটি null রেফারেন্স রাখার লোভ সামলাতে পারিনি, কারণ এটি বাস্তবায়ন করা খুব সহজ ছিল। এটি অগণিত ভুল, দুর্বলতা এবং সিস্টেম ক্র্যাশের কারণ হয়েছে, যা সম্ভবত গত চল্লিশ বছরে এক বিলিয়ন ডলারের কষ্ট ও ক্ষতির কারণ হয়েছে।

null মানের সমস্যা হলো যে যদি আপনি একটি null মানকে not-null মান হিসাবে ব্যবহার করার চেষ্টা করেন, তবে আপনি কোনো না কোনো ধরনের একটি error পাবেন। যেহেতু এই null বা not-null বৈশিষ্ট্যটি সর্বব্যাপী, তাই এই ধরনের ভুল করা অত্যন্ত সহজ।

তবে, null যে ধারণাটি প্রকাশ করার চেষ্টা করে তা এখনও একটি দরকারী ধারণা: একটি null হলো এমন একটি মান যা বর্তমানে কোনো কারণে অবৈধ বা অনুপস্থিত।

সমস্যাটি আসলে ধারণার সাথে নয়, বরং নির্দিষ্ট বাস্তবায়নের সাথে। তাই, Rust-এ null নেই, তবে এটিতে একটি enum রয়েছে যা একটি মানের উপস্থিতি বা অনুপস্থিতির ধারণাটি এনকোড করতে পারে। এই enum-টি হলো Option<T>, এবং এটি স্ট্যান্ডার্ড লাইব্রেরি দ্বারা ডিফাইন করা হয়েছে এভাবে:

#![allow(unused)]
fn main() {
enum Option<T> {
    None,
    Some(T),
}
}

Option<T> enum-টি এতটাই দরকারী যে এটি প্রিলিউডেও অন্তর্ভুক্ত করা হয়েছে; আপনাকে এটিকে স্পষ্টভাবে স্কোপে আনতে হবে না। এর variant-গুলোও প্রিলিউডে অন্তর্ভুক্ত: আপনি Option:: উপসর্গ ছাড়াই সরাসরি Some এবং None ব্যবহার করতে পারেন। Option<T> enum-টি এখনও একটি সাধারণ enum, এবং Some(T)None এখনও Option<T> type-এর variant।

<T> সিনট্যাক্সটি Rust-এর একটি বৈশিষ্ট্য যা নিয়ে আমরা এখনও কথা বলিনি। এটি একটি জেনেরিক টাইপ প্যারামিটার, এবং আমরা চ্যাপ্টার ১০-এ জেনেরিক সম্পর্কে আরও বিস্তারিত আলোচনা করব। আপাতত, আপনাকে শুধু জানতে হবে যে <T> মানে Option enum-এর Some variant যেকোনো type-এর ডেটার একটি অংশ ধারণ করতে পারে, এবং T-এর পরিবর্তে ব্যবহৃত প্রতিটি সুনির্দিষ্ট type সামগ্রিক Option<T> type-কে একটি ভিন্ন type-এ পরিণত করে। এখানে সংখ্যা এবং ক্যারেক্টার type ধারণ করার জন্য Option মান ব্যবহার করার কিছু উদাহরণ দেওয়া হলো:

fn main() {
    let some_number = Some(5);
    let some_char = Some('e');

    let absent_number: Option<i32> = None;
}

some_number-এর type হলো Option<i32>some_char-এর type হলো Option<char>, যা একটি ভিন্ন type। Rust এই type-গুলো অনুমান করতে পারে কারণ আমরা Some variant-এর ভিতরে একটি মান নির্দিষ্ট করেছি। absent_number-এর জন্য, Rust আমাদের সামগ্রিক Option type-টি annotate করতে বলে: compiler শুধুমাত্র একটি None মান দেখে সংশ্লিষ্ট Some variant কোন type ধারণ করবে তা অনুমান করতে পারে না। এখানে, আমরা Rust-কে বলি যে আমরা absent_number-কে Option<i32> type-এর হিসাবে বোঝাতে চাই।

যখন আমাদের কাছে একটি Some মান থাকে, আমরা জানি যে একটি মান উপস্থিত আছে এবং মানটি Some-এর ভিতরে রয়েছে। যখন আমাদের কাছে একটি None মান থাকে, তখন এটি某种 অর্থে null-এর মতোই: আমাদের কাছে একটি বৈধ মান নেই। তাহলে Option<T> থাকাটা null থাকার চেয়ে ভালো কেন?

সংক্ষেপে, কারণ Option<T> এবং T (যেখানে T যেকোনো type হতে পারে) ভিন্ন type, তাই compiler আমাদের একটি Option<T> মানকে এমনভাবে ব্যবহার করতে দেবে না যেন এটি অবশ্যই একটি বৈধ মান। উদাহরণস্বরূপ, এই কোডটি কম্পাইল হবে না, কারণ এটি একটি i8-কে একটি Option<i8>-এর সাথে যোগ করার চেষ্টা করছে:

fn main() {
    let x: i8 = 5;
    let y: Option<i8> = Some(5);

    let sum = x + y;
}

যদি আমরা এই কোডটি চালাই, আমরা এই ধরনের একটি error বার্তা পাই:

$ cargo run
   Compiling enums v0.1.0 (file:///projects/enums)
error[E0277]: cannot add `Option<i8>` to `i8`
 --> src/main.rs:5:17
  |
5 |     let sum = x + y;
  |                 ^ no implementation for `i8 + Option<i8>`
  |
  = help: the trait `Add<Option<i8>>` is not implemented for `i8`
  = help: the following other types implement trait `Add<Rhs>`:
            `&i8` implements `Add<i8>`
            `&i8` implements `Add`
            `i8` implements `Add<&i8>`
            `i8` implements `Add`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `enums` (bin "enums") due to 1 previous error

ব্যাপক! কার্যতঃ, এই error বার্তাটির অর্থ হলো Rust বুঝতে পারছে না কিভাবে একটি i8 এবং একটি Option<i8> যোগ করতে হয়, কারণ তারা ভিন্ন type। যখন আমাদের কাছে Rust-এ i8-এর মতো কোনো type-এর মান থাকে, তখন compiler নিশ্চিত করবে যে আমাদের কাছে সবসময় একটি বৈধ মান আছে। আমরা সেই মানটি ব্যবহার করার আগে null-এর জন্য পরীক্ষা না করেই আত্মবিশ্বাসের সাথে এগিয়ে যেতে পারি। শুধুমাত্র যখন আমাদের কাছে একটি Option<i8> থাকে (বা আমরা যে ধরনের মান নিয়ে কাজ করছি), তখনই আমাদের একটি মান না থাকার সম্ভাবনা নিয়ে চিন্তা করতে হয়, এবং compiler নিশ্চিত করবে যে আমরা মানটি ব্যবহার করার আগে সেই পরিস্থিতিটি হ্যান্ডেল করি।

অন্য কথায়, T অপারেশন সম্পাদন করার আগে আপনাকে একটি Option<T>-কে T-তে রূপান্তর করতে হবে। সাধারণত, এটি null-এর সবচেয়ে সাধারণ সমস্যাগুলির মধ্যে একটি ধরতে সাহায্য করে: কোনো কিছুকে not-null ধরে নেওয়া যখন এটি আসলে null

একটি not-null মান ভুলভাবে ধরে নেওয়ার ঝুঁকি দূর করা আপনাকে আপনার কোডের প্রতি আরও আত্মবিশ্বাসী হতে সাহায্য করে। এমন একটি মান পেতে যা সম্ভবত null হতে পারে, আপনাকে স্পষ্টভাবে সেই মানের type-কে Option<T> করে অপ্ট-ইন করতে হবে। তারপরে, যখন আপনি সেই মানটি ব্যবহার করেন, তখন মানটি null হলে সেই কেসটি স্পষ্টভাবে হ্যান্ডেল করতে হবে। যেখানেই একটি মানের type Option<T> নয়, আপনি নিরাপদে ধরে নিতে পারেন যে মানটি null নয়। এটি Rust-এর একটি ইচ্ছাকৃত ডিজাইন সিদ্ধান্ত ছিল null-এর ব্যাপকতা সীমিত করতে এবং Rust কোডের নিরাপত্তা বৃদ্ধি করতে।

তাহলে আপনি কিভাবে Some variant থেকে T মানটি বের করবেন যখন আপনার কাছে Option<T> type-এর একটি মান থাকে, যাতে আপনি সেই মানটি ব্যবহার করতে পারেন? Option<T> enum-এর অনেক মেথড রয়েছে যা বিভিন্ন পরিস্থিতিতে দরকারী; আপনি এর ডকুমেন্টেশনে সেগুলি দেখতে পারেন। Option<T>-এর মেথডগুলোর সাথে পরিচিত হওয়া Rust-এর সাথে আপনার যাত্রায় অত্যন্ত দরকারী হবে।

সাধারণভাবে, একটি Option<T> মান ব্যবহার করার জন্য, আপনার এমন কোড থাকা দরকার যা প্রতিটি variant হ্যান্ডেল করবে। আপনি চান কিছু কোড শুধুমাত্র তখনই চলুক যখন আপনার কাছে একটি Some(T) মান থাকে, এবং এই কোডটি ভিতরের T ব্যবহার করার অনুমতি পায়। আপনি চান অন্য কিছু কোড শুধুমাত্র তখনই চলুক যদি আপনার কাছে একটি None মান থাকে, এবং সেই কোডের কাছে একটি T মান উপলব্ধ থাকে না। match এক্সপ্রেশন একটি কন্ট্রোল ফ্লো কনস্ট্রাক্ট যা enum-এর সাথে ব্যবহার করা হলে ঠিক এই কাজটিই করে: এটি enum-এর কোন variant-টি পেয়েছে তার উপর নির্ভর করে ভিন্ন কোড চালাবে, এবং সেই কোডটি ম্যাচিং মানের ভিতরের ডেটা ব্যবহার করতে পারে।