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

panic! দিয়ে অপুনরুদ্ধারযোগ্য এরর (Unrecoverable Errors)

কখনও কখনও আপনার কোডে খারাপ কিছু ঘটে, এবং আপনি তা নিয়ে কিছুই করতে পারেন না। এই ধরনের ক্ষেত্রে, Rust-এ panic! ম্যাক্রো রয়েছে। বাস্তবে প্যানিক (panic) ঘটানোর দুটি উপায় আছে: এমন কোনো কাজ করা যা আমাদের কোডকে প্যানিক করায় (যেমন একটি array-এর সীমার বাইরে অ্যাক্সেস করা) অথবা স্পষ্টভাবে panic! ম্যাক্রো কল করা। উভয় ক্ষেত্রেই, আমরা আমাদের প্রোগ্রামে একটি প্যানিক ঘটাই। ডিফল্টভাবে, এই প্যানিকগুলো একটি ব্যর্থতার বার্তা প্রিন্ট করবে, স্ট্যাক আনওয়াইন্ড (unwind) করবে, পরিষ্কার করবে এবং প্রোগ্রাম থেকে বেরিয়ে যাবে। একটি এনভায়রনমেন্ট ভেরিয়েবলের মাধ্যমে, আপনি প্যানিকের উৎস খুঁজে বের করা সহজ করার জন্য প্যানিক ঘটলে Rust-কে কল স্ট্যাক (call stack) প্রদর্শন করাতেও পারেন।

প্যানিকের প্রতিক্রিয়ায় স্ট্যাক আনওয়াইন্ড করা বা অ্যাবোর্ট করা (Unwinding the Stack or Aborting in Response to a Panic)

ডিফল্টভাবে, যখন একটি প্যানিক ঘটে, প্রোগ্রামটি unwinding শুরু করে, যার মানে হলো Rust স্ট্যাকের উপরে ফিরে যায় এবং প্রতিটি ফাংশন থেকে ডেটা পরিষ্কার করে। তবে, এভাবে ফিরে যাওয়া এবং পরিষ্কার করা অনেক কাজ। তাই Rust আপনাকে অবিলম্বে aborting (বন্ধ করা) এর বিকল্প বেছে নেওয়ার সুযোগ দেয়, যা কোনো কিছু পরিষ্কার না করেই প্রোগ্রামটি শেষ করে দেয়।

প্রোগ্রাম যে মেমরি ব্যবহার করছিল তা তখন অপারেটিং সিস্টেম দ্বারা পরিষ্কার করার প্রয়োজন হবে। যদি আপনার প্রকল্পে ফলস্বরূপ বাইনারিটিকে যতটা সম্ভব ছোট করার প্রয়োজন হয়, তবে আপনি আপনার Cargo.toml ফাইলের উপযুক্ত [profile] বিভাগে panic = 'abort' যোগ করে প্যানিকের সময় unwinding থেকে aborting-এ স্যুইচ করতে পারেন। উদাহরণস্বরূপ, যদি আপনি রিলিজ মোডে প্যানিকের সময় অ্যাবোর্ট করতে চান, তবে এটি যোগ করুন:

[profile.release]
panic = 'abort'

আসুন একটি সহজ প্রোগ্রামে panic! কল করার চেষ্টা করি:

fn main() {
    panic!("crash and burn");
}

আপনি যখন প্রোগ্রামটি চালাবেন, তখন আপনি এইরকম কিছু দেখতে পাবেন:

$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/panic`

thread 'main' panicked at src/main.rs:2:5:
crash and burn
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

panic!-এর কলটি শেষ দুটি লাইনে থাকা এরর বার্তাটির কারণ। প্রথম লাইনটি আমাদের প্যানিক বার্তা এবং আমাদের সোর্স কোডের সেই স্থানটি দেখায় যেখানে প্যানিক ঘটেছে: src/main.rs:2:5 নির্দেশ করে যে এটি আমাদের src/main.rs ফাইলের দ্বিতীয় লাইনের পঞ্চম অক্ষর।

এই ক্ষেত্রে, নির্দেশিত লাইনটি আমাদের কোডের অংশ, এবং যদি আমরা সেই লাইনে যাই, আমরা panic! ম্যাক্রো কলটি দেখতে পাই। অন্যান্য ক্ষেত্রে, panic! কলটি এমন কোডে থাকতে পারে যা আমাদের কোড কল করে, এবং এরর বার্তা দ্বারা রিপোর্ট করা ফাইলের নাম এবং লাইন নম্বর অন্য কারো কোডের হবে যেখানে panic! ম্যাক্রো কল করা হয়েছে, আমাদের কোডের সেই লাইন নয় যা অবশেষে panic! কলের কারণ হয়েছে।

আমরা panic! কলটি যে ফাংশনগুলো থেকে এসেছে তার ব্যাকট্রেস (backtrace) ব্যবহার করে আমাদের কোডের কোন অংশটি সমস্যার কারণ তা খুঁজে বের করতে পারি। একটি panic! ব্যাকট্রেস কীভাবে ব্যবহার করতে হয় তা বোঝার জন্য, আসুন আরেকটি উদাহরণ দেখি এবং দেখি যখন আমাদের কোডের কোনো বাগের কারণে কোনো লাইব্রেরি থেকে panic! কল আসে, আমাদের কোড সরাসরি ম্যাক্রো কল করার পরিবর্তে, তখন কেমন হয়। লিস্টিং ৯-১ এ কিছু কোড রয়েছে যা একটি ভেক্টরের বৈধ ইনডেক্সের সীমার বাইরে একটি ইনডেক্স অ্যাক্সেস করার চেষ্টা করে।

fn main() {
    let v = vec![1, 2, 3];

    v[99];
}

এখানে, আমরা আমাদের ভেক্টরের ১০০তম এলিমেন্টটি (যা ইনডেক্স ৯৯-এ রয়েছে কারণ ইনডেক্সিং শূন্য থেকে শুরু হয়) অ্যাক্সেস করার চেষ্টা করছি, কিন্তু ভেক্টরটিতে মাত্র তিনটি এলিমেন্ট রয়েছে। এই পরিস্থিতিতে, Rust প্যানিক করবে। [] ব্যবহার করার কথা একটি এলিমেন্ট রিটার্ন করা, কিন্তু যদি আপনি একটি অবৈধ ইনডেক্স পাস করেন, তবে এখানে এমন কোনো এলিমেন্ট নেই যা Rust সঠিকভাবে রিটার্ন করতে পারত।

C ভাষায়, একটি ডেটা স্ট্রাকচারের শেষের বাইরে পড়ার চেষ্টা করা আনডিফাইন্ড বিহেভিয়ার (undefined behavior)। আপনি মেমরির সেই অবস্থানে যা কিছু আছে তা পেতে পারেন যা ডেটা স্ট্রাকচারের সেই এলিমেন্টের সাথে সঙ্গতিপূর্ণ, যদিও মেমরিটি সেই স্ট্রাকচারের অন্তর্গত নয়। এটিকে buffer overread বলা হয় এবং এটি নিরাপত্তা দুর্বলতার কারণ হতে পারে যদি কোনো আক্রমণকারী ইনডেক্সটিকে এমনভাবে ম্যানিপুলেট করতে সক্ষম হয় যাতে সে এমন ডেটা পড়তে পারে যা তার পড়ার অনুমতি নেই এবং যা ডেটা স্ট্রাকচারের পরে সংরক্ষণ করা হয়েছে।

আপনার প্রোগ্রামকে এই ধরনের দুর্বলতা থেকে রক্ষা করার জন্য, যদি আপনি এমন একটি ইনডেক্সে একটি এলিমেন্ট পড়ার চেষ্টা করেন যা বিদ্যমান নেই, Rust এক্সিকিউশন বন্ধ করে দেবে এবং চলতে অস্বীকার করবে। আসুন এটি চেষ্টা করে দেখি:

$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/panic`

thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace```

এই এররটি আমাদের _main.rs_ এর ৪ নম্বর লাইনকে নির্দেশ করে যেখানে আমরা `v` ভেক্টরের ইনডেক্স `99` অ্যাক্সেস করার চেষ্টা করছি।

`note:` লাইনটি আমাদের বলছে যে আমরা `RUST_BACKTRACE` এনভায়রনমেন্ট ভেরিয়েবল সেট করে এররের কারণ কী ঘটেছে তার একটি সঠিক ব্যাকট্রেস পেতে পারি। একটি _ব্যাকট্রেস_ হলো এই পয়েন্টে পৌঁছানোর জন্য কল করা সমস্ত ফাংশনের একটি তালিকা। Rust-এ ব্যাকট্রেস অন্যান্য ল্যাঙ্গুয়েজের মতোই কাজ করে: ব্যাকট্রেস পড়ার মূল চাবিকাঠি হলো উপর থেকে শুরু করে পড়া যতক্ষণ না আপনি আপনার লেখা ফাইল দেখতে পান। সেখানেই সমস্যার উৎপত্তি। সেই স্থানের উপরের লাইনগুলো হলো কোড যা আপনার কোড কল করেছে; নীচের লাইনগুলো হলো কোড যা আপনার কোডকে কল করেছে। এই আগের এবং পরের লাইনগুলিতে কোর Rust কোড, স্ট্যান্ডার্ড লাইব্রেরি কোড, বা আপনার ব্যবহার করা ক্রেট অন্তর্ভুক্ত থাকতে পারে। আসুন `RUST_BACKTRACE` এনভায়রনমেন্ট ভেরিয়েবলকে `0` ছাড়া যেকোনো মান দিয়ে সেট করে একটি ব্যাকট্রেস পাওয়ার চেষ্টা করি। লিস্টিং ৯-২ আপনার যা দেখার সম্ভাবনা তার অনুরূপ আউটপুট দেখায়।

<!-- manual-regeneration
cd listings/ch09-error-handling/listing-09-01
RUST_BACKTRACE=1 cargo run
copy the backtrace output below
check the backtrace number mentioned in the text below the listing
-->

<Listing number="9-2" caption="`RUST_BACKTRACE` এনভায়রনমেন্ট ভেরিয়েবল সেট করা হলে `panic!` কলের দ্বারা তৈরি ব্যাকট্রেস">

```console
$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
stack backtrace:
   0: rust_begin_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
   1: core::panicking::panic_fmt
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
   2: core::panicking::panic_bounds_check
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:273:5
   3: <usize as core::slice::index::SliceIndex<[T]>>::index
             at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:274:10
   4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
             at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:16:9
   5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
             at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3361:9
   6: panic::main
             at ./src/main.rs:4:6
   7: core::ops::function::FnOnce::call_once
             at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

এটি অনেক বড় আউটপুট! আপনার অপারেটিং সিস্টেম এবং Rust সংস্করণের উপর নির্ভর করে আপনি যে সঠিক আউটপুটটি দেখবেন তা ভিন্ন হতে পারে। এই তথ্য সহ ব্যাকট্রেস পেতে, ডিবাগ চিহ্ন (debug symbols) সক্রিয় থাকতে হবে। cargo build বা cargo run ব্যবহার করার সময় --release ফ্ল্যাগ ছাড়া ডিবাগ চিহ্ন ডিফল্টভাবে সক্রিয় থাকে, যেমনটি আমরা এখানে করেছি।

লিস্টিং ৯-২ এর আউটপুটে, ব্যাকট্রেসের ৬ নম্বর লাইনটি আমাদের প্রকল্পের সেই লাইনটিকে নির্দেশ করে যা সমস্যার কারণ: src/main.rs এর ৪ নম্বর লাইন। যদি আমরা আমাদের প্রোগ্রামকে প্যানিক করতে না চাই, তবে আমাদের তদন্ত শুরু করা উচিত আমাদের লেখা একটি ফাইলের উল্লেখ করা প্রথম লাইন দ্বারা নির্দেশিত অবস্থান থেকে। লিস্টিং ৯-১-এ, যেখানে আমরা ইচ্ছাকৃতভাবে এমন কোড লিখেছিলাম যা প্যানিক করবে, প্যানিক ঠিক করার উপায় হলো ভেক্টরের ইনডেক্সের সীমার বাইরের কোনো এলিমেন্ট অনুরোধ না করা। ভবিষ্যতে যখন আপনার কোড প্যানিক করবে, তখন আপনাকে বের করতে হবে কোডটি কোন মান দিয়ে কোন কাজটি করার কারণে প্যানিক করছে এবং কোডের পরিবর্তে কী করা উচিত।

আমরা এই অধ্যায়ের পরে “To panic! or Not to panic! বিভাগে panic! এবং কখন আমাদের এরর পরিস্থিতি হ্যান্ডেল করার জন্য panic! ব্যবহার করা উচিত এবং কখন উচিত নয় সে বিষয়ে ফিরে আসব। এর পরে, আমরা দেখব কীভাবে Result ব্যবহার করে একটি এরর থেকে পুনরুদ্ধার করা যায়।