HeadlinesBriefing favicon HeadlinesBriefing.com

রাস্টে পার্স নন-ভ্যালিডেট: নন-এম্পটি ভেক্টর

Hacker News •
×

অনেক প্রোগ্রামারের মতো, আমি এ্যালেক্সিস কিংয়ের 'পার্স নন-ভ্যালিডেট' নিবন্ধটি খুব আকর্ষণীয় বলে মনে করি, কারণ এটি একটি পরিচিত এবং গুরুত্বপূর্ণ অভ্যাসকে নাম দেয়, যা আমি আগে দেখেছি এবং ব্যবহার করেছি কিন্তু স্পষ্টভাবে নাম দিইনি। এই নিবন্ধটি 'পার্স নন-ভ্যালিডেট' প্যাটার্নের একটি পর্যালোচনা, যা রাস্ট প্রোগ্রামিং ভাষায় প্রয়োগ করা হয়েছে (মূল নিবন্ধে হ্যাসকেলের ব্যবহার করা হয়েছে)। আমি রাস্ট স্ট্যান্ডার্ড লাইব্রেরি এবং অন্যান্য বিখ্যাত প্রজেক্টগুলিতে এই প্যাটার্নের শিক্ষামূলক উদাহরণগুলি খুঁজে বের করতে বিশেষভাবে আগ্রহী ছিলাম। মূল নিবন্ধটি পুনরাবৃত্তি না করে (দয়া করে প্রথমে এটি পড়ুন!), এখানে এর মূল বিষয়বস্তু রয়েছে। উদাহরণস্বরূপ, বিখ্যাত Vec-এর কথা ভাবুন; এর প্রথম মেথডটি Option<&T> রিটার্ন করে। কেন? কারণ একটি ভেক্টরে কোনও উপাদান থাকার গ্যারান্টি নেই, তাই যদি first-কে একটি খালি ভেক্টরে কল করা হয় তবে কী হবে? এই ক্ষেত্রে একটি Option রিটার্ন করা রাস্টে একটি আদর্শ অভ্যাস [1], যেখানে Option রিটার্ন করা ফাংশনগুলির ফলাফল গ্রহণ করার জন্য একটি সুবিধাজনক সিনট্যাক্স সুগার রয়েছে এবং পরবর্তী পদক্ষেপটি কী করতে হবে তা নির্ধারণ করা যায়। তাহলে সমস্যাটি কী? ধরি আমাদের একটি ফাংশন আছে যা একটি পরিবেশ ভেরিয়েবল থেকে কিছু কনফিগারেশন পাথগুলি পড়ে, এবং এটি নিশ্চিত করে যে তালিকাটি খালি হতে পারে না: use anyhow::{Result, ensure}; fn get_configuration_directories() -> Result<Vec<Path Buf>> { let value = env::var("CONFIG_DIRS").context("could not read CONFIG_DIRS")?; let directories: Vec<Path Buf> = value.split(',').map(str::trim).map(Path Buf::from).collect(); ensure!(!directories.is_empty(), "empty CONFIG_DIRS"); Ok(directories) } এখন পর্যন্ত, সব ঠিক আছে। এখন এই ফাংশনটির একটি সাধারণ ব্যবহার দেখুন: fn main() -> Result<()> { let config_dirs = get_configuration_directories()?; match config_dirs.first() { Some(cache_dir) => initialize_cache(cache_dir), None => unreachable!("already checked that CONFIG_DIRS is non-empty"); } Ok(()) } একবার get_configuration_directories একটি সফল ফলাফল রিটার্ন করলে, আমরা নিশ্চিত হই যে ভেক্টরটি খালি নয়। কিন্তু যদি আমরা এই ভেক্টরটির প্রথম উপাদানটি পেতে চাই, তবে আমাদের first মেথডটি ব্যবহার করতে হবে, যা Option<&T> রিটার্ন করে। তাই, আমরা আবারও একটি সম্ভাব্যভাবে খালি ক্ষেত্রে (যেখানে অপশনটি None) মোকাবেলা করতে বাধ্য হই। মূল নিবন্ধটি বলে যে, এটি কোডের স্পষ্টতা, সম্ভাব্য পারফরম্যান্স প্রভাব এবং একটি টিকিং টাইম বোমার ক্ষেত্রে অনেক সমস্যা সৃষ্টি করে, যদি কখনও get_configuration_directories-এ ইনভারিয়েন্টটি পরিবর্তন করা হয়। মূল সমস্যাটি হল যে Vec মূলত একটি টাইপ যা খালি হতে পারে; আমরা সমস্ত প্রাসঙ্গিক কোডে একটি 'এটি খালি হতে পারে না, পিঙ্কি প্রমিস!' মন্তব্য লিখতে পারি, কিন্তু এটি কোনও কিছু দ্বারা আনুষ্ঠানিকভাবে যাচাই করা হয় না। একটি 'নন-এম্পটি' ভেক্টরের জন্য একটি টাইপ একটি 'নন-এম্পটি' ভেক্টরের জন্য একটি টাইপ হল সমাধান যা একটি নতুন প্রতিষ্ঠিত ইনভারিয়েন্টকে প্রয়োগ করার জন্য টাইপ সিস্টেমের ব্যবহারকে কাজে লাগায়। আমরা একটি পৃথক টাইপ ব্যবহার করতে পারি যা 'একটি ভেক্টর যা খালি হতে পারে না' এর প্রতিনিধিত্ব করে; আসলে, এই ধরনের টাইপগুলি ইতিমধ্যেই বেশ কয়েকটি রাস্ট ক্রেটগুলিতে বিদ্যমান - উদাহরণস্বরূপ, nonempty: pub struct Non Empty<T> { pub head: T, pub tail: Vec<T>, } এই টাইপটির কোনও কনস্ট্রাক্টর নেই যা 'কোনও উপাদান নেই' এর অনুমতি দেয়; এর new একটি উপাদান নেয়, এবং এর first মেথডটি Option ছাড়াই &T রিটার্ন করে: pub const fn new(e: T) -> Self { Self::singleton(e) } pub const fn singleton(head: T) -> Self { Non Empty { head, tail: Vec::new() } } pub const fn first(&self) -> &T { &self.head } ক্রেটটির বাকি অংশটি Non Empty-কে একটি সাধারণ Vec-এর যতটা সম্ভব কাছাকাছি হতে দেয়, অনেকগুলি দরকারী ট্রেটগুলি বাস্তবায়ন করে, এবং নিম্নলিখিত রূপান্তরগুলি: pub fn from_vec(mut vec: Vec<T>) -> Option<Non Empty<T>> { if vec.is_empty() { None } else { let head = vec.remove(0); Some(Non Empty { head, tail: vec }) } } এখন, আমাদের get_configuration_directories ফাংশনটি যদি একটি সাধারণ Vec-এর পরিবর্তে একটি Non Empty রিটার্ন করে তবে এটি কেমন হবে তা দেখুন: fn get_configuration_directories() -> Result<Non Empty<Path Buf>> { let value = env::var("CONFIG_DIRS").context("could not read CONFIG_DIRS")?; let directories = value.split(',').map(str::trim).map(Path Buf::from).collect(); let Some(directories) = Non Empty::from_vec(directories) else { bail!("CONFIG_DIRS cannot be empty"); }; Ok(directories) } এখানে Non Empty::from_vec-এর ব্যবহার লক্ষ্য করুন - এটি সেই জায়গা যেখানে ইনভারিয়েন্ট প্রতিষ্ঠিত হয়। এখন...

FAQ প্রশ্ন: রাস্টে একটি নন-এম্পটি ভেক্টর টাইপ ব্যবহার করার প্রধান সুবিধা কী?

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