রিফিউটেবিলিটি: একটি প্যাটার্ন মেলে কিনা (Refutability: Whether a Pattern Might Fail to Match)

প্যাটার্ন দুটি রূপে আসে: রিফিউটেবল (refutable) এবং ইরিফিউটেবল (irrefutable)। যে প্যাটার্নগুলি যে কোনও সম্ভাব্য মানের জন্য মিলবে সেগুলি হল ইরিফিউটেবল। একটি উদাহরণ হবে let x = 5; স্টেটমেন্টের x, কারণ x যেকোনো কিছুর সাথে মেলে এবং তাই মেলতে ব্যর্থ হতে পারে না। যে প্যাটার্নগুলি কিছু সম্ভাব্য মানের জন্য মেলতে ব্যর্থ হতে পারে সেগুলি হল রিফিউটেবল। একটি উদাহরণ হবে if let Some(x) = a_value এক্সপ্রেশনের Some(x), কারণ যদি a_value ভেরিয়েবলের মান Some-এর পরিবর্তে None হয়, তাহলে Some(x) প্যাটার্নটি মিলবে না।

ফাংশন প্যারামিটার, let স্টেটমেন্ট এবং for লুপগুলি কেবল ইরিফিউটেবল প্যাটার্ন গ্রহণ করতে পারে, কারণ মানগুলি না মিললে প্রোগ্রামটি অর্থপূর্ণ কিছু করতে পারে না। if let এবং while let এক্সপ্রেশন এবং let-else স্টেটমেন্ট রিফিউটেবল এবং ইরিফিউটেবল প্যাটার্ন গ্রহণ করে, কিন্তু কম্পাইলার ইরিফিউটেবল প্যাটার্নের বিরুদ্ধে সতর্ক করে কারণ সংজ্ঞা অনুসারে সেগুলি সম্ভাব্য ব্যর্থতা পরিচালনা করার উদ্দেশ্যে তৈরি: একটি কন্ডিশনালের কার্যকারিতা হল সফলতা বা ব্যর্থতার উপর নির্ভর করে ভিন্নভাবে কাজ করার ক্ষমতা।

সাধারণভাবে, রিফিউটেবল এবং ইরিফিউটেবল প্যাটার্নের মধ্যে পার্থক্য নিয়ে আপনার চিন্তা করার দরকার নেই; যাইহোক, আপনাকে রিফিউটেবিলিটির ধারণার সাথে পরিচিত হতে হবে যাতে আপনি কোনও error মেসেজে এটি দেখলে প্রতিক্রিয়া জানাতে পারেন। সেই ক্ষেত্রগুলিতে, আপনাকে কোডের অভিপ্রেত আচরণের উপর নির্ভর করে প্যাটার্ন বা আপনি যে গঠনের সাথে প্যাটার্নটি ব্যবহার করছেন সেটি পরিবর্তন করতে হবে।

আসুন একটি উদাহরণের দিকে তাকাই যেখানে আমরা একটি রিফিউটেবল প্যাটার্ন ব্যবহার করার চেষ্টা করি যেখানে Rust-এর একটি ইরিফিউটেবল প্যাটার্ন প্রয়োজন এবং এর বিপরীতে কী ঘটে। Listing 19-8 একটি let স্টেটমেন্ট দেখায়, কিন্তু প্যাটার্নের জন্য আমরা Some(x) নির্দিষ্ট করেছি, একটি রিফিউটেবল প্যাটার্ন। আপনি যেমন আশা করতে পারেন, এই কোডটি কম্পাইল হবে না।

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-8/src/main.rs:here}}

যদি some_option_value একটি None মান হয়, তাহলে এটি Some(x) প্যাটার্নের সাথে মিলতে ব্যর্থ হবে, অর্থাৎ প্যাটার্নটি রিফিউটেবল। যাইহোক, let স্টেটমেন্ট শুধুমাত্র একটি ইরিফিউটেবল প্যাটার্ন গ্রহণ করতে পারে কারণ None মান দিয়ে কোডটি বৈধভাবে কিছুই করতে পারে না। কম্পাইল করার সময়, Rust অভিযোগ করবে যে আমরা একটি রিফিউটেবল প্যাটার্ন ব্যবহার করার চেষ্টা করেছি যেখানে একটি ইরিফিউটেবল প্যাটার্ন প্রয়োজন:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
 --> src/main.rs:3:9
  |
3 |     let Some(x) = some_option_value;
  |         ^^^^^^^ pattern `None` not covered
  |
  = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
  = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
  = note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
  |
3 |     let Some(x) = some_option_value else { todo!() };
  |                                     ++++++++++++++++

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

যেহেতু আমরা Some(x) প্যাটার্ন দিয়ে প্রতিটি বৈধ মান কভার করিনি (এবং করতেও পারিনি!), তাই Rust সঙ্গতভাবেই একটি কম্পাইলার error তৈরি করে।

যদি আমাদের কাছে একটি রিফিউটেবল প্যাটার্ন থাকে যেখানে একটি ইরিফিউটেবল প্যাটার্ন প্রয়োজন, তাহলে আমরা প্যাটার্ন ব্যবহার করা কোড পরিবর্তন করে এটিকে ঠিক করতে পারি: let ব্যবহার করার পরিবর্তে, আমরা if let ব্যবহার করতে পারি। তারপর যদি প্যাটার্নটি না মেলে, তাহলে কোডটি কেবল কোঁকড়া বন্ধনীর ভিতরের কোডটি এড়িয়ে যাবে, এটিকে বৈধভাবে চালিয়ে যাওয়ার একটি উপায় দেবে। Listing 19-9 দেখায় কিভাবে Listing 19-8-এর কোড ঠিক করা যায়।

fn main() {
    let some_option_value: Option<i32> = None;
    if let Some(x) = some_option_value {
        println!("{x}");
    }
}

আমরা কোডটিকে একটি আউট দিয়েছি! এই কোডটি এখন সম্পূর্ণরূপে বৈধ। যাইহোক, যদি আমরা if let-কে একটি ইরিফিউটেবল প্যাটার্ন (এমন একটি প্যাটার্ন যা সর্বদা মিলবে), যেমন x, দিই, যেমনটি Listing 19-10-এ দেখানো হয়েছে, তাহলে কম্পাইলার একটি সতর্কতা দেবে।

fn main() {
    if let x = 5 {
        println!("{x}");
    };
}

Rust অভিযোগ করে যে একটি ইরিফিউটেবল প্যাটার্নের সাথে if let ব্যবহার করার কোনও মানে হয় না:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
 --> src/main.rs:2:8
  |
2 |     if let x = 5 {
  |        ^^^^^^^^^
  |
  = note: this pattern will always match, so the `if let` is useless
  = help: consider replacing the `if let` with a `let`
  = note: `#[warn(irrefutable_let_patterns)]` on by default

warning: `patterns` (bin "patterns") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/patterns`
5

এই কারণে, ম্যাচ আর্মগুলিকে অবশ্যই রিফিউটেবল প্যাটার্ন ব্যবহার করতে হবে, শেষ আর্মটি বাদে, যেটি একটি ইরিফিউটেবল প্যাটার্ন দিয়ে অবশিষ্ট সমস্ত মানের সাথে মেলানো উচিত। Rust আমাদের শুধুমাত্র একটি আর্ম সহ একটি match-এ একটি ইরিফিউটেবল প্যাটার্ন ব্যবহার করার অনুমতি দেয়, কিন্তু এই সিনট্যাক্সটি বিশেষভাবে দরকারী নয় এবং এটি একটি সরল let স্টেটমেন্ট দিয়ে প্রতিস্থাপিত করা যেতে পারে।

এখন আপনি জানেন যে কোথায় প্যাটার্ন ব্যবহার করতে হবে এবং রিফিউটেবল এবং ইরিফিউটেবল প্যাটার্নের মধ্যে পার্থক্য কী, আসুন প্যাটার্ন তৈরি করতে আমরা যে সমস্ত সিনট্যাক্স ব্যবহার করতে পারি তা কভার করি।