panic!
দিয়ে আনরিকভারেবল এরর (Unrecoverable Errors with panic!
)
কখনও কখনও আপনার কোডে খারাপ কিছু ঘটে এবং আপনি এটি সম্পর্কে কিছুই করতে পারেন না। এই ক্ষেত্রগুলোতে, Rust-এর panic!
ম্যাক্রো রয়েছে। কার্যত প্যানিক ঘটানোর দুটি উপায় রয়েছে: এমন কোনো কাজ করা যা আমাদের কোডকে প্যানিক করে (যেমন অ্যারের শেষের বাইরে অ্যাক্সেস করা) অথবা স্পষ্টতই panic!
ম্যাক্রো কল করা। উভয় ক্ষেত্রেই, আমরা আমাদের প্রোগ্রামে একটি প্যানিক ঘটাই। ডিফল্টরূপে, এই প্যানিকগুলো একটি ব্যর্থতার মেসেজ প্রিন্ট করবে, আনওয়াইন্ড (unwind) করবে, স্ট্যাক পরিষ্কার করবে এবং বন্ধ হয়ে যাবে। একটি এনভায়রনমেন্ট ভেরিয়েবলের মাধ্যমে, আপনি Rust-কে প্যানিক ঘটলে কল স্ট্যাক (call stack) প্রদর্শন করতে বলতে পারেন যাতে প্যানিকের উৎস খুঁজে বের করা সহজ হয়।
প্যানিকের প্রতিক্রিয়ায় স্ট্যাক আনওয়াইন্ড করা বা অ্যাবোর্ট করা (Unwinding the Stack or Aborting in Response to a Panic)
ডিফল্টরূপে, যখন একটি প্যানিক ঘটে তখন প্রোগ্রামটি আনওয়াইন্ডিং (unwinding) শুরু করে, যার অর্থ হল Rust স্ট্যাকের উপরে উঠে যায় এবং প্রতিটি ফাংশনের ডেটা পরিষ্কার করে। যাইহোক, ফিরে যাওয়া এবং পরিষ্কার করা অনেক কাজ। তাই, Rust আপনাকে অবিলম্বে অ্যাবোর্টিং (aborting)-এর বিকল্প বেছে নেওয়ার অনুমতি দেয়, যা পরিষ্কার না করেই প্রোগ্রামটি শেষ করে।
প্রোগ্রামটি যে মেমরি ব্যবহার করছিল তা অপারেটিং সিস্টেম দ্বারা পরিষ্কার করতে হবে। যদি আপনার প্রোজেক্টে আপনাকে ফলাফল বাইনারিটিকে যতটা সম্ভব ছোট করতে হয়, তাহলে আপনি আপনার Cargo.toml ফাইলের উপযুক্ত
[profile]
বিভাগেpanic = 'abort'
যোগ করে প্যানিক হওয়ার পরে আনওয়াইন্ডিং থেকে অ্যাবোর্টিং-এ পরিবর্তন করতে পারেন। উদাহরণস্বরূপ, আপনি যদি রিলিজ মোডে প্যানিকের সময় অ্যাবোর্ট করতে চান তবে এটি যোগ করুন:[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!
কলের ফাংশনগুলোর ব্যাকট্রেস ব্যবহার করে আমাদের কোডের কোন অংশটি সমস্যার কারণ তা বের করতে পারি। panic!
ব্যাকট্রেস কীভাবে ব্যবহার করতে হয় তা বোঝার জন্য, আসুন আরেকটি উদাহরণ দেখি এবং দেখি যখন আমাদের কোডের সরাসরি ম্যাক্রো কল করার পরিবর্তে আমাদের কোডের কোনো বাগের কারণে একটি লাইব্রেরি থেকে panic!
কল আসে তখন এটি কেমন হয়। Listing 9-1-এ কিছু কোড রয়েছে যা ভেক্টরের বৈধ ইনডেক্সের সীমার বাইরে একটি ইনডেক্স অ্যাক্সেস করার চেষ্টা করে।
fn main() { let v = vec![1, 2, 3]; v[99]; }
এখানে, আমরা আমাদের ভেক্টরের 100তম এলিমেন্টটি অ্যাক্সেস করার চেষ্টা করছি (যা 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
এনভায়রনমেন্ট ভেরিয়েবল সেট করে এররটির কারণ কী ঘটেছিল তার একটি ব্যাকট্রেস পেতে পারি। একটি ব্যাকট্রেস (backtrace) হল সেই সমস্ত ফাংশনগুলোর একটি তালিকা যা এই পয়েন্ট পর্যন্ত কল করা হয়েছে। Rust-এ ব্যাকট্রেসগুলো অন্যান্য ভাষার মতোই কাজ করে: ব্যাকট্রেস পড়ার মূল চাবিকাঠি হল ওপর থেকে শুরু করা এবং আপনি যে ফাইলগুলো লিখেছেন সেগুলো না দেখা পর্যন্ত পড়া। সেটি হল সেই স্থান যেখানে সমস্যাটির উদ্ভব হয়েছে। সেই স্থানের উপরের লাইনগুলো হল কোড যা আপনার কোড কল করেছে; নিচের লাইনগুলো হল সেই কোড যা আপনার কোডকে কল করেছে। এই আগের এবং পরের লাইনগুলোতে কোর Rust কোড, স্ট্যান্ডার্ড লাইব্রেরি কোড বা আপনি যে ক্রেটগুলো ব্যবহার করছেন সেগুলো অন্তর্ভুক্ত থাকতে পারে। আসুন RUST_BACKTRACE
এনভায়রনমেন্ট ভেরিয়েবলটিকে 0
ছাড়া অন্য কোনো মানে সেট করে একটি ব্যাকট্রেস পাওয়ার চেষ্টা করি। Listing 9-2 আপনি যা দেখবেন তার অনুরূপ আউটপুট দেখায়।
$ 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) সক্রিয় থাকতে হবে। ডিবাগ সিম্বলগুলো ডিফল্টরূপে সক্রিয় থাকে যখন --release
ফ্ল্যাগ ছাড়া cargo build
বা cargo run
ব্যবহার করা হয়, যেমনটি আমরা এখানে করেছি।
Listing 9-2-এর আউটপুটে, ব্যাকট্রেসের লাইন ৬ আমাদের প্রোজেক্টের সেই লাইনের দিকে নির্দেশ করে যা সমস্যার কারণ: src/main.rs-এর লাইন ৪। আমরা যদি আমাদের প্রোগ্রামটিকে প্যানিক করতে না চাই, তাহলে আমাদের লেখা একটি ফাইলের উল্লেখ করা প্রথম লাইন দ্বারা নির্দেশিত অবস্থানে আমাদের অনুসন্ধান শুরু করা উচিত। Listing 9-1-এ, যেখানে আমরা ইচ্ছাকৃতভাবে কোড লিখেছি যা প্যানিক করবে, প্যানিক ঠিক করার উপায় হল ভেক্টরের ইনডেক্সের সীমার বাইরের কোনো এলিমেন্টের অনুরোধ না করা। ভবিষ্যতে যখন আপনার কোড প্যানিক করবে, তখন আপনাকে বের করতে হবে যে কোডটি কী অ্যাকশন নিচ্ছে এবং কী মান নিয়ে প্যানিক ঘটাচ্ছে এবং কোডের পরিবর্তে কী করা উচিত।
আমরা panic!
-এ ফিরে আসব এবং কখন আমাদের এরর পরিস্থিতি হ্যান্ডেল করার জন্য panic!
ব্যবহার করা উচিত এবং কখন করা উচিত নয়, এই চ্যাপ্টারের “panic!
নাকি panic!
নয়” বিভাগে। এরপর, আমরা দেখব কিভাবে Result
ব্যবহার করে একটি এরর থেকে পুনরুদ্ধার করা যায়।