পাঠ ৯.৩

কখন panic! করব, কখন না

To panic! or Not to panic!

এই পাঠে নীতি — কখন panic! করা সঠিক, কখন Result return করা।

Default — Result

Function fail করতে পারে — তখন default choice হলো Result return করা। কারণ — panic-এ recover-এর কোনো সুযোগ নেই, তুমি caller-এর হয়ে decide করছ যে এটা unrecoverable। Result return করলে caller-ই বাছবে — recover করবে না নিজেই panic করবে।

কখন panic চলে — example, prototype, test

  • Example code — concept demo করছ; unwrap/expect placeholder, real app-এ ভালো error handling বসবে।
  • Prototype — পরে robust error handling add করার আগে quickly কাজ করানো।
  • Test — কোনো method fail করলে test-ই fail হয়; panic হলো test-fail-এর mechanism।

যখন তুমি compiler-এর চেয়ে বেশি জানো

কোনো logic দিয়ে নিশ্চিত — Result সবসময় Ok। কিন্তু compiler সেই guarantee verify করতে পারছে না। তখন expect ঠিক — এবং message-এ কারণ document করো।

use std::net::IpAddr;

let home: IpAddr = "127.0.0.1"
    .parse()
    .expect("Hardcoded IP address should be valid");

"127.0.0.1" hardcoded — valid IP, কিন্তু parse Result return করে। Compiler জানে না এই string সবসময় valid। expect-এর message-এ assumption লিখে রাখলাম — পরে user input থেকে এলে message দেখে বুঝবো robust handling দরকার।

Panic কখন appropriate

Code এমন bad state-এ পৌঁছেছে — এই rule-গুলোর এক বা একাধিক satisfy করলে panic-ই উপযুক্ত:

  1. Bad state unexpected — মাঝে মাঝে হবে এমন কিছু না (যেমন user wrong format-এ data দিল — সেটা expected)।
  2. পরের code এই state-এ relying করতে চায় না — প্রতি step-এ check করার চেয়ে এক জায়গায় enforce করা ভালো।
  3. Type-এ encode করার ভালো উপায় নেই

Library design

User তোমার library-কে invalid value দিলে — Result দাও, user decide করুক। কিন্তু value invalid হলে continuing insecure বা harmful হবে — তখন panic! দিয়ে programmer-কে alert করো (এটা তাদের bug)।

Function-এর contract (কী input valid) violate হলে panic — এটা caller-side bug। Document-এ contract clearly বলো।

Result কখন

Failure expected — return Result। যেমন:

  • Parser malformed data পেল।
  • HTTP request rate-limit বা timeout।
  • File না পাওয়া।

Type system দিয়ে validate

Function-এ বারবার check না করে — Rust-এর type system কাজে লাগাও। যেমন:

  • Option<T>-এর জায়গায় T নাও — তাহলে None case handle-ই করতে হবে না।
  • Negative হতে পারে না এমন কিছু — i32-এর জায়গায় u32

Custom type দিয়ে validation

1-100-এর মধ্যে guess চাই। প্রতি function-এ check না করে — একটা type বানাও যাতে সবসময় valid value:

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {value}.");
        }

        Guess { value }
    }

    pub fn value(&self) -> i32 {
        self.value
    }
}
  • value field private — বাইরে থেকে set করা যাবে না।
  • Guess::new() validation চালায়। Bypass-এর উপায় নেই।
  • .value() getter — read access।

এখন কোনো function Guess parameter নিলে আর range check-এর দরকার নেই — type-ই guarantee দিচ্ছে। এটাই "valid by construction" pattern।

এই পাঠ থেকে যা শিখলে

  • Default — Result; caller-কে option দাও।
  • Panic — example, prototype, test, contract violation, security risk-এ।
  • Compiler-এর চেয়ে বেশি জানলে — expect with documenting message।
  • Type system দিয়ে validate করো — Option বনাম T, u32 বনাম i32
  • Validated custom type (Guess pattern) — constructor-এ check, পরে free use।