সংক্ষিপ্ত উত্তর: সর্বাধিক নমনীয়তার জন্য, আপনি FnMutকলব্যাক টাইপের জেনেরিক কলব্যাক সেটার সহ একটি বক্সযুক্ত অবজেক্ট হিসাবে কলব্যাক সঞ্চয় করতে পারেন । এর জন্য কোডটি উত্তরের শেষ উদাহরণে দেখানো হয়েছে। আরও বিশদ ব্যাখ্যার জন্য পড়ুন।
"ফাংশন পয়েন্টার": কলব্যাকস হিসাবে fn
প্রশ্নের সি ++ কোডের নিকটতম সমতুল্য কলব্যাককে এক fnপ্রকার হিসাবে ঘোষণা করবে । fnমূলশব্দ দ্বারা নির্ধারিত ফাংশনগুলি encapsulates fn, অনেকটা সি ++ এর ফাংশন পয়েন্টারগুলির মতো:
type Callback = fn();
struct Processor {
callback: Callback,
}
impl Processor {
fn set_callback(&mut self, c: Callback) {
self.callback = c;
}
fn process_events(&self) {
(self.callback)();
}
}
fn simple_callback() {
println!("hello world!");
}
fn main() {
let p = Processor {
callback: simple_callback,
};
p.process_events();
}
এই কোডটি Option<Box<Any>>ফাংশনের সাথে যুক্ত "ব্যবহারকারীর ডেটা" রাখতে একটি অন্তর্ভুক্ত করার জন্য বাড়ানো যেতে পারে । তবুও, এটি মূর্খতাযুক্ত মরিচা হবে না। কোনও ফাংশনের সাথে ডেটা যুক্ত করার মরিচা উপায় হ'ল এটি কোনও বেনামে বন্ধ করে দেওয়া আধুনিক সি ++ এর মতো । যেহেতু ক্লোজারগুলি নয় fn, তাই set_callbackঅন্য ধরণের ফাংশন অবজেক্ট গ্রহণ করতে হবে।
জেনেরিক ফাংশন অবজেক্ট হিসাবে কলব্যাক
জাস্ট এবং সি ++ উভয় ক্ষেত্রে একই কল স্বাক্ষরের সাথে ক্লোজারগুলি বিভিন্ন আকারে আসে যাতে তারা বিভিন্ন মান গ্রহণ করতে পারে mod অতিরিক্তভাবে, প্রতিটি ক্লোজার সংজ্ঞা ক্লোজারের মানটির জন্য একটি অনন্য বেনামি প্রকার উত্পন্ন করে। এই বাধাগুলির কারণে, স্ট্রাকটি এর প্রকারের নাম দিতে পারে নাcallback ক্ষেত্রের , বা এটি কোনও উপ নাম ব্যবহার করতে পারে না।
কংক্রিটের ধরণের উল্লেখ না করে স্ট্রাক্ট ফিল্ডে একটি ক্লোজার এম্বেড করার একটি উপায় হ'ল স্ট্রাকটি জেনেরিক তৈরি করা । কাঠামোটি স্বয়ংক্রিয়ভাবে এর আকার এবং কংক্রিটের কার্যকারিতা বা বন্ধ করার জন্য কলব্যাকের ধরণেরটিকে স্বয়ংক্রিয়ভাবে গ্রহণ করবে:
struct Processor<CB>
where
CB: FnMut(),
{
callback: CB,
}
impl<CB> Processor<CB>
where
CB: FnMut(),
{
fn set_callback(&mut self, c: CB) {
self.callback = c;
}
fn process_events(&mut self) {
(self.callback)();
}
}
fn main() {
let s = "world!".to_string();
let callback = || println!("hello {}", s);
let mut p = Processor { callback: callback };
p.process_events();
}
আগের মতোই, কলব্যাকের নতুন সংজ্ঞাটি সংজ্ঞায়িত শীর্ষ-স্তরের ফাংশনগুলি গ্রহণ করতে সক্ষম হবে fn, তবে এইটি ক্লোসারের || println!("hello world!")পাশাপাশি মানগুলি যেমন ক্যাপচারগুলিও গ্রহণ করবে || println!("{}", somevar)। এই কারণে প্রসেসরের userdataকলব্যাকের সাথে যাওয়ার দরকার নেই ; কলারের সরবরাহকারী বন্ধটি set_callbackতার পরিবেশ থেকে প্রয়োজনীয় ডেটাগুলি স্বয়ংক্রিয়ভাবে ক্যাপচার করবে এবং অনুরোধ করা হলে তা উপলব্ধ হবে।
তবে এর সাথে FnMutকেন ডিল হয় , শুধু কেন নয় Fn? যেহেতু ক্লোজারগুলি ক্যাপচারিত মানগুলিকে ধারণ করে, তাই বন্ধের ডাক দেওয়ার সময় মরিচাটির স্বাভাবিক পরিব্যক্তি বিধিগুলি প্রয়োগ করতে হবে। ক্লোজারগুলি তাদের ধরে থাকা মানগুলির সাথে কী করে তার উপর নির্ভর করে তাদের তিনটি পরিবারে গ্রুপ করা হয়, যার প্রতিটি বৈশিষ্ট্যযুক্ত:
Fnক্লোজারগুলি হ'ল কেবল ডেটা পড়ে এবং সম্ভবত একাধিক থ্রেড থেকে সম্ভবত একাধিকবার কল করা যেতে পারে। উপরের উভয় ক্লোজার হ'লFn ।
FnMutক্লোজারগুলি হ'ল ডেটা সংশোধন করে, উদাহরণস্বরূপ ক্যাপচারড mutভেরিয়েবলটিতে লিখে । এগুলি একাধিকবার বলা যেতে পারে তবে সমান্তরালে নয়। (আহ্বান কFnMut একাধিক থ্রেড থেকে ক্লোজার করা একটি ডেটা রেসের দিকে পরিচালিত করে, সুতরাং এটি কেবলমাত্র একটি মিটেক্সের সুরক্ষার সাহায্যে করা যেতে পারে)) ক্লোজার অবজেক্টটি কলার দ্বারা পরিবর্তনীয় ঘোষণা করতে হবে।
FnOnceবন্ধ যে গ্রাস কিছু আদ্যাশক্তি ডেটা তারা একটি ফাংশন যে তার মালিকানা নেয় করার জন্য একটি বন্দী মান সরিয়ে ক্যাপচার যেমন। নামটি থেকে বোঝা যায়, এগুলি কেবল একবার কল করা যেতে পারে এবং ফোনকারীকে অবশ্যই তাদের মালিক হতে হবে।
কিছুটা পাল্টাপাল্টি স্বজ্ঞাতভাবে, যখন কোনও অবসন্নতার বিষয়টি গ্রহণ করে এমন কোনও বস্তুর প্রকারের জন্য নির্দিষ্ট হওয়া নির্দিষ্ট বৈশিষ্ট্য নির্দিষ্ট করা হয়, তখন FnOnceএটি আসলে সবচেয়ে অনুমোদিত। জেনেরিক কলব্যাক টাইপের FnOnceবৈশিষ্ট্যটি অবশ্যই পূরণ করতে হবে তা ঘোষণার অর্থ এটি আক্ষরিক কোনও বন্ধকে মেনে নেবে । তবে এটি একটি দাম সহ আসে: এর অর্থ হোল্ডারকে কেবল একবার কল করার অনুমতি দেওয়া হয়েছে। যেহেতু process_events()কলব্যাকটি একাধিকবার ডাকার বিকল্প বেছে নিতে পারে এবং পদ্ধতিটি নিজেই একাধিকবার ডাকা হতে পারে, পরবর্তী সবচেয়ে অনুমতিপ্রাপ্ত সীমাবদ্ধ FnMut। নোট করুন যে আমাদের process_eventsমিউটেশন হিসাবে চিহ্নিত করতে হয়েছিল self।
নন-জেনেরিক কলব্যাক্স: ফাংশন বৈশিষ্ট্যযুক্ত বস্তু
কলব্যাকের জেনেরিক বাস্তবায়ন অত্যন্ত দক্ষ হলেও এর ইন্টারফেসের গুরুতর সীমাবদ্ধতা রয়েছে। এটি প্রতিটি Processorক্ষেত্রে একটি কংক্রিট কলব্যাক প্রকারের সাথে প্যারামিটারাইজেশন করা প্রয়োজন , যার অর্থ একটি একক Processorকেবলমাত্র একক কলব্যাক প্রকারের সাথে ডিল করতে পারে। প্রতিটি বন্ধের একটি পৃথক ধরণের বৈশিষ্ট্য দেওয়া হয়েছে, জেনেরিক অনুসরণ করে Processorপরিচালনা করতে পারে না । দুটি কলব্যাক ক্ষেত্রগুলিকে সমর্থন করার জন্য কাঠামোটি প্রসারিত করার জন্য পুরো কাঠামোটিকে দুটি ধরণের প্যারামিটারাইজেশন করা দরকার, যা কলব্যাকের সংখ্যা বাড়ার সাথে সাথে তা খুব শীঘ্রই অস্বাস্থ্যকর হয়ে উঠবে। কলব্যাকের সংখ্যা গতিশীল হওয়ার প্রয়োজন হলে আরও প্রকারের প্যারামিটার যুক্ত করা কাজ করবে না, উদাহরণস্বরূপ এমন কোনও ফাংশন যা বিভিন্ন কলব্যাকের ভেক্টর বজায় রাখে তা প্রয়োগ করতে ।proc.set_callback(|| println!("hello"))proc.set_callback(|| println!("world"))add_callback
প্রকারের প্যারামিটারটি অপসারণ করতে, আমরা বৈশিষ্ট্যযুক্ত বস্তুর , জং এর বৈশিষ্ট্যটি ব্যবহার করতে পারি যা বৈশিষ্ট্যের ভিত্তিতে গতিশীল ইন্টারফেসগুলি স্বয়ংক্রিয়ভাবে তৈরি করতে দেয়। এটিকে কখনও কখনও টাইপ ইরেজর হিসাবে উল্লেখ করা হয় এবং এটি সি ++ [1] [2] এর একটি জনপ্রিয় কৌশল , যা জাভা এবং এফপি ভাষার 'শব্দটির কিছুটা পৃথক ব্যবহারের সাথে বিভ্রান্ত না হওয়ার জন্য। সি ++ এর সাথে পরিচিত পাঠকগণ একটি কার্যকরীকরণ Fnএবং কোনও Fnবৈশিষ্ট্য অবজেক্টকে সাধারণ ফাংশন অবজেক্ট এবং এর মধ্যে পার্থক্যের সমতুল্য হিসাবে পার্থক্যটি সনাক্ত করতে পারবেনstd::function সি ++ এর মানগুলির মধ্যে ।
&অপারেটরের কাছে কোনও জিনিস ধার করে এবং নির্দিষ্ট বৈশিষ্ট্যের রেফারেন্সে কাস্টিং বা জোর করে একটি বৈশিষ্ট্যযুক্ত বস্তু তৈরি করা হয় । এই ক্ষেত্রে, যেহেতু Processorকলব্যাক অবজেক্টের মালিকানা প্রয়োজন, আমরা orrowণ গ্রহণ করতে পারি না, তবে অবশ্যই কলব্যাকটি একটি হিপ-বরাদ্দকৃত Box<dyn Trait>(মরিচা সমমানের std::unique_ptr) মধ্যে সংরক্ষণ করতে হবে, যা কোনও বৈশিষ্ট্যযুক্ত বস্তুর সাথে কার্যত সমতুল্য।
যদি Processorসঞ্চয় হয় তবে Box<dyn FnMut()>এটি আর জেনেরিক হওয়ার দরকার নেই, তবে set_callback পদ্ধতিটি এখন cএকটি impl Traitআর্গুমেন্টের মাধ্যমে জেনেরিকে গ্রহণ করে । যেমন, এটা রাষ্ট্রের সঙ্গে বন্ধ সহ callable কোন ধরনের, গ্রহণ করতে পারে, এবং সঠিকভাবে এটা সংরক্ষণকারী আগেই বাক্স Processor। থেকে জেনেরিক যুক্তি set_callbackসীমিত নেই প্রসেসর, গ্রহণ কলব্যাক ধরনের কি নামে গৃহীত কলব্যাক ধরণ ধরণ সঞ্চিত থেকে পৃথক করা হয় Processorstruct হয়।
struct Processor {
callback: Box<dyn FnMut()>,
}
impl Processor {
fn set_callback(&mut self, c: impl FnMut() + 'static) {
self.callback = Box::new(c);
}
fn process_events(&mut self) {
(self.callback)();
}
}
fn simple_callback() {
println!("hello");
}
fn main() {
let mut p = Processor {
callback: Box::new(simple_callback),
};
p.process_events();
let s = "world!".to_string();
let callback2 = move || println!("hello {}", s);
p.set_callback(callback2);
p.process_events();
}
বক্সড ক্লোজারের অভ্যন্তরে রেফারেন্সের লাইফটাইম
'staticসারা জীবনের ধরনের উপর আবদ্ধ cযুক্তি দ্বারা গৃহীত set_callbackকম্পাইলার যে সন্তুষ্ট করার একটি সহজ উপায় হল রেফারেন্স অন্তর্ভুক্ত মধ্যে c, যা একটি অবসান যে তার পরিবেশ বোঝায় হতে পারে, শুধুমাত্র বিশ্বব্যাপী মান পড়ুন এবং সেইজন্য ব্যবহার সর্বত্র বৈধ থাকবে কলব্যাক তবে স্থির আবদ্ধটি খুব ভারী হাতে: যদিও এটি ক্লোজারগুলি স্বীকার করে যেগুলি নিজের বস্তুগুলির ঠিক জরিমানা (যা আমরা moveউপরেরটি বন্ধ করে নিশ্চিত করেছি ), এটি ক্লোজারগুলি প্রত্যাখ্যান করে যা স্থানীয় পরিবেশের সাথে সম্পর্কিত, এমনকি যখন তারা কেবল মানগুলি উল্লেখ করে তবে প্রসেসরের আউটলাইভ এবং প্রকৃতপক্ষে নিরাপদ হবে।
যেহেতু প্রসেসর বেঁচে আছে কেবল আমাদের কেবল কলব্যাকগুলি জীবিত রয়েছে, আমাদের তাদের জীবনকাল প্রসেসরের সাথে বেঁধে দেওয়ার চেষ্টা করা উচিত, যা তার চেয়ে কম কঠোর আবদ্ধ 'static। তবে আমরা যদি কেবল 'staticআবদ্ধ জীবনকাল সরিয়ে set_callbackফেলি তবে এটি আর সঙ্কলিত হয় না। এটি কারণ set_callbackএকটি নতুন বাক্স তৈরি করে এবং callbackক্ষেত্রের হিসাবে এটি নির্ধারিত হয় Box<dyn FnMut()>। যেহেতু সংজ্ঞাটি বাক্সযুক্ত বৈশিষ্ট্য অবজেক্টের জন্য একটি আজীবন নির্দিষ্ট করে না, 'staticতা বোঝানো হয় এবং এই কার্যভারটি কার্যকরভাবে আজীবন প্রশস্ত করে তুলবে (কলব্যাকের একটি নামহীন স্বেচ্ছাসেবী জীবনকাল থেকে 'static), যা অনুমোদিত নয়। সমাধানটি হ'ল প্রসেসরের জন্য একটি সুস্পষ্ট আজীবন সরবরাহ করা এবং বাক্সে থাকা রেফারেন্স এবং কলব্যাকের রেফারেন্সগুলি উভয়ের সাথে বাক্সে আবদ্ধ করা set_callback:
struct Processor<'a> {
callback: Box<dyn FnMut() + 'a>,
}
impl<'a> Processor<'a> {
fn set_callback(&mut self, c: impl FnMut() + 'a) {
self.callback = Box::new(c);
}
}
এই জীবনকালগুলি সুস্পষ্টভাবে তৈরি হওয়ার সাথে সাথে এটি আর ব্যবহারের প্রয়োজন হয় না 'static। ক্লোজারটি এখন স্থানীয় sঅবজেক্টকে বোঝায় , অর্থাত্ আর আর থাকতে হবে না moveতবে শর্তটি প্রসেসরের আউটলাইভ করে তা নিশ্চিত করার sসংজ্ঞা দেওয়ার আগে সংজ্ঞা দেওয়া pহয়।
CBহতে হবে'staticচূড়ান্ত উদাহরণে?