এর একটি সাধারণ বাস্তবায়ন দেখুন :
struct Parent {
count: u32,
}
struct Child<'a> {
parent: &'a Parent,
}
struct Combined<'a> {
parent: Parent,
child: Child<'a>,
}
impl<'a> Combined<'a> {
fn new() -> Self {
let parent = Parent { count: 42 };
let child = Child { parent: &parent };
Combined { parent, child }
}
}
fn main() {}
এটি ত্রুটির সাথে ব্যর্থ হবে:
error[E0515]: cannot return value referencing local variable `parent`
--> src/main.rs:19:9
|
17 | let child = Child { parent: &parent };
| ------- `parent` is borrowed here
18 |
19 | Combined { parent, child }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of `parent` because it is borrowed
--> src/main.rs:19:20
|
14 | impl<'a> Combined<'a> {
| -- lifetime `'a` defined here
...
17 | let child = Child { parent: &parent };
| ------- borrow of `parent` occurs here
18 |
19 | Combined { parent, child }
| -----------^^^^^^---------
| | |
| | move out of `parent` occurs here
| returning this value requires that `parent` is borrowed for `'a`
এই ত্রুটিটি পুরোপুরি বুঝতে, আপনাকে ভাবতে হবে যে কীভাবে মানগুলি মেমরিতে উপস্থাপিত হয় এবং আপনি যখন
এই মানগুলি স্থানান্তর করেন তখন কী ঘটে । আসুন Combined::new
কিছু অনুমানমূলক মেমরি ঠিকানা দিয়ে টীকায়িত করুন যা দেখায় যে মানগুলি কোথায় রয়েছে:
let parent = Parent { count: 42 };
// `parent` lives at address 0x1000 and takes up 4 bytes
// The value of `parent` is 42
let child = Child { parent: &parent };
// `child` lives at address 0x1010 and takes up 4 bytes
// The value of `child` is 0x1000
Combined { parent, child }
// The return value lives at address 0x2000 and takes up 8 bytes
// `parent` is moved to 0x2000
// `child` is ... ?
কি হবে child
? মানটি যদি ঠিক সেইভাবে স্থানান্তরিত parent
হয়, তবে এটি মেমরিটিকে উল্লেখ করবে যে এতে আর একটি বৈধ মান থাকার গ্যারান্টি নেই। কোডের অন্য কোনও অংশে মেমরি ঠিকানার 0x1000 এ মান সংরক্ষণের অনুমতি দেওয়া হয়। এটি একটি পূর্ণসংখ্যা হিসাবে ধরে নেওয়া স্মৃতিতে অ্যাক্সেস করা ক্র্যাশ এবং / অথবা সুরক্ষা বাগের দিকে নিয়ে যেতে পারে এবং মরচে প্রতিরোধকারী ত্রুটির অন্যতম প্রধান বিষয়।
এটি হ'ল সমস্যাটি যা আজীবন প্রতিরোধ করে। একটি জীবনকাল হ'ল কিছুটা মেটাডেটা যা আপনাকে এবং সংকলকটিকে এটির বর্তমান মেমরির স্থানে কতক্ষণ মান কার্যকর হবে তা জানার অনুমতি দেয় । এটি একটি গুরুত্বপূর্ণ পার্থক্য, কারণ এটি মরচে আগতদের একটি সাধারণ ভুল। মরিচা লাইফটাইম কোনও সময় তৈরির সময় নয় যখন কোনও বস্তু তৈরি হয় এবং কখন এটি ধ্বংস হয়!
সাদৃশ্য হিসাবে, এটিকে এভাবে ভাবুন: কোনও ব্যক্তির জীবনের সময় এগুলি অনেকগুলি পৃথক স্থানে বাস করবে, যার প্রত্যেকটির একটি আলাদা ঠিকানা থাকবে with একটি মরিয়া জীবনকাল আপনি বর্তমানে যে ঠিকানাটিতে রয়েছেন তার সাথে সম্পর্কিত, ভবিষ্যতে আপনি কখনই মারা যাবেন তা নিয়ে নয় (যদিও মৃত্যুবরণও আপনার ঠিকানা পরিবর্তন করে)। প্রতিবার আপনি সরানোর সময় এটি প্রাসঙ্গিক কারণ আপনার ঠিকানাটি আর বৈধ নয়।
এটাও গুরুত্বপূর্ণ যে জীবনকাল আপনার কোড পরিবর্তন করে না ; আপনার কোডটি লাইফটাইমগুলি নিয়ন্ত্রণ করে, আপনার লাইফটাইম কোডটি নিয়ন্ত্রণ করে না। করুণ কথায় হ'ল "জীবনকাল বর্ণনামূলক, প্রেসক্রিপটিভ নয়"।
আসুন Combined::new
আমরা কয়েকটি লাইনের নম্বর দিয়ে টীকাযুক্ত করি যা আমরা লাইফ টাইম হাইলাইট করতে ব্যবহার করব:
{ // 0
let parent = Parent { count: 42 }; // 1
let child = Child { parent: &parent }; // 2
// 3
Combined { parent, child } // 4
} // 5
কংক্রিট জীবদ্দশায় এর parent
1 থেকে 4 হয়, সমেত (যা আমি প্রতিনিধিত্ব করব [1,4]
)। এর কংক্রিট জীবনকাল child
হয় [2,4]
, এবং বিনিময়ে মূল্যের কংক্রিট জীবনকাল হয় [4,5]
। শূন্য থেকে শুরু হওয়া কংক্রিট লাইফটাইমগুলি পাওয়া সম্ভব - যা কোনও ক্রিয়াকলাপ বা ব্লকের বাইরে থাকা কিছুতে প্যারামিটারের আজীবন প্রতিনিধিত্ব করবে।
মনে রাখবেন যে, এর জীবদ্দশায় child
নিজেই [2,4]
, কিন্তু যে এটা বোঝায় একটি জীবনকাল সঙ্গে একটি মান [1,4]
। রেফার-টু মানটি করার আগে যতক্ষণ রেফারিং মানটি অবৈধ হয়ে যায় এটি ঠিক আছে fine সমস্যাটি তখন ঘটে যখন আমরা child
ব্লক থেকে ফিরে আসার চেষ্টা করি । এটি তার প্রাকৃতিক দৈর্ঘ্যের বাইরে জীবনকালকে "অতিরিক্ত-প্রসারিত" করবে।
এই নতুন জ্ঞানের প্রথম দুটি উদাহরণ ব্যাখ্যা করা উচিত। তৃতীয়টির বাস্তবায়নের দিকে তাকাতে হবে Parent::child
। সম্ভাবনাগুলি হ'ল এটি দেখতে এরকম কিছু দেখাবে:
impl Parent {
fn child(&self) -> Child { /* ... */ }
}
সুস্পষ্ট জেনেরিক আজীবন প্যারামিটারগুলি লেখা এড়াতে এটি আজীবন এলিজেন ব্যবহার করে । এটি সমান:
impl Parent {
fn child<'a>(&'a self) -> Child<'a> { /* ... */ }
}
উভয় ক্ষেত্রেই, পদ্ধতিটি বলে যে একটি Child
কাঠামো ফিরে আসবে যা কংক্রিটের আজীবন প্যারামিটারাইজড ছিল
self
। অন্যভাবে বলা হয়েছে, Child
উদাহরণটিতে Parent
এটি তৈরি করা হয়েছে এমন একটি উল্লেখ রয়েছে এবং এটি Parent
উদাহরণের চেয়ে বেশি দিন বাঁচতে পারে না
।
এটি আমাদের সনাক্ত করতে দেয় যে আমাদের তৈরির কার্যক্রমে কিছু সত্যই ভুল:
fn make_combined<'a>() -> Combined<'a> { /* ... */ }
যদিও আপনি এই লিখিতটিকে অন্য কোনও আকারে দেখার সম্ভাবনা বেশি:
impl<'a> Combined<'a> {
fn new() -> Combined<'a> { /* ... */ }
}
উভয় ক্ষেত্রেই কোনও আর্গুমেন্টের মাধ্যমে কোনও আজীবন প্যারামিটার সরবরাহ করা হচ্ছে না। এর অর্থ হ'ল আজীবন যা Combined
প্যারামিটারাইজ করা হবে তা কোনও কিছুর দ্বারা সীমাবদ্ধ নয় - কলার এটি যা চায় তা হতে পারে। এটি অযৌক্তিক, কারণ কলার 'static
আজীবন নির্দিষ্ট করে দিতে পারে এবং এই শর্তটি পূরণ করার কোনও উপায় নেই।
আমি কীভাবে এটি ঠিক করব?
সবচেয়ে সহজ এবং সর্বাধিক প্রস্তাবিত সমাধান হ'ল এই আইটেমগুলিকে একই কাঠামোয় একসাথে রাখার চেষ্টা না করা। এটি করে আপনার কাঠামো বাসা বাঁধাই আপনার কোডের লাইফটাইমকে নকল করবে। এক সাথে কাঠামোর মধ্যে ডেটার মালিক এমন প্রকারগুলি রাখুন এবং তারপরে এমন পদ্ধতিগুলি সরবরাহ করুন যা আপনাকে প্রয়োজনীয় হিসাবে রেফারেন্স যুক্ত রেফারেন্স বা অবজেক্টগুলি পেতে দেয়।
একটি বিশেষ ক্ষেত্রে রয়েছে যেখানে আজীবন ট্র্যাকিং অত্যধিক alousর্ষান্বিত হয়: যখন আপনার স্তূপে কিছু রাখা হয়। Box<T>
উদাহরণস্বরূপ আপনি যখন এটি ব্যবহার করেন এটি ঘটে
। এই ক্ষেত্রে, যে কাঠামোটি সরানো হয় তাতে গাদা মধ্যে একটি পয়েন্টার থাকে। পয়েন্ট-এ মানটি স্থিতিশীল থাকবে তবে পয়েন্টারের ঠিকানাটি নিজেই সরে যাবে। অনুশীলনে, এটি কোনও ব্যাপার নয়, আপনি সর্বদা পয়েন্টারটি অনুসরণ করেন।
ভাড়া বাক্স (আর পরিচালিত বা সমর্থিত) বা owning_ref বাক্স এই ক্ষেত্রে প্রতিনিধিত্ব উপায় আছে, কিন্তু তারা প্রয়োজন যে বেস ঠিকানা সরাতে না । এটি মিউটেশন ভেক্টরগুলিকে বাতিল করে দেয়, যার ফলে পুনরায় স্থান নির্ধারণ এবং হিপ-বরাদ্দ মানগুলির সরানো হতে পারে।
ভাড়া নিয়ে সমস্যার সমাধানের উদাহরণ:
অন্যান্য ক্ষেত্রে, আপনি কোনও প্রকারের রেফারেন্স-কাউন্টিংয়ে যেতে চান, যেমন ব্যবহার করে Rc
বা Arc
।
অধিক তথ্য
parent
স্ট্রাক্টে স্থানান্তরিত হওয়ার পরে , সংকলক কেন নতুন কাঠামোর সাথে parent
এটি উল্লেখ করতে এবং child
স্ট্রাক্টে এটি নির্ধারণ করতে সক্ষম হয় না?
তাত্ত্বিকভাবে এটি করা সম্ভব হলেও এটি করা বিপুল পরিমাণ জটিলতা এবং ওভারহেডের পরিচয় দেয়। প্রতিবার অবজেক্টটি সরানো হয়েছে, সংকলকটির রেফারেন্সটি "ফিক্স আপ" করতে কোড inোকানো দরকার। এর অর্থ হ'ল স্ট্রাক্ট অনুলিপি করা এখন খুব সস্তা অপারেশন নয় যা কিছু বিটকে সরিয়ে দেয়। এমনকি এটির অর্থ এইও হতে পারে যে এই জাতীয় কোডটি ব্যয়বহুল, একটি অনুমানমূলক অপ্টিমাইজারটি কতটা ভাল হবে তার উপর নির্ভর করে:
let a = Object::new();
let b = a;
let c = b;
এটি প্রতিটি পদক্ষেপের জন্য ঘটতে বাধ্য করার পরিবর্তে প্রোগ্রামার চয়ন করতে সক্ষম হয় যে কখন এমন পদ্ধতি তৈরি করে যখন আপনি তাদের ডাকবেন তখন উপযুক্ত রেফারেন্স নেবে by
নিজেই একটি রেফারেন্স সহ একটি টাইপ
একটি নির্দিষ্ট ক্ষেত্রে রয়েছে যেখানে আপনি নিজের প্রসঙ্গে একটি প্রকার তৈরি করতে পারেন । Option
এটি দুটি ধাপে তৈরি করার মতো আপনাকে এমন কিছু ব্যবহার করতে হবে যদিও:
#[derive(Debug)]
struct WhatAboutThis<'a> {
name: String,
nickname: Option<&'a str>,
}
fn main() {
let mut tricky = WhatAboutThis {
name: "Annabelle".to_string(),
nickname: None,
};
tricky.nickname = Some(&tricky.name[..4]);
println!("{:?}", tricky);
}
এটি কিছুটা অর্থে কাজ করে তবে তৈরি মানটি সীমাবদ্ধ - এটি কখনই সরানো যায় না। উল্লেখযোগ্যভাবে, এর অর্থ এটি কোনও ফাংশন থেকে ফিরে পাওয়া যায় না বা কোনও কিছুর কাছে মূল্য-দিয়ে দেওয়া যায় না। একটি কনস্ট্রাক্টর ফাংশন লাইফটাইমগুলির সাথে উপরের মতো একই সমস্যা দেখায়:
fn creator<'a>() -> WhatAboutThis<'a> { /* ... */ }
কি হবে Pin
?
Pin
, মরিচা 1.33 এ স্থিতিশীল, মডিউল ডকুমেন্টেশনে এটি রয়েছে :
এই জাতীয় দৃশ্যের একটি প্রধান উদাহরণ স্ব-রেফারেন্টিয়াল স্ট্রাক্ট তৈরি করা হবে, যেহেতু পয়েন্টারযুক্ত কোনও বস্তুটিকে নিজের দিকে সরানো এগুলি অকার্যকর করে দেবে, যা অপরিবর্তিত আচরণের কারণ হতে পারে।
এটি উল্লেখ করা গুরুত্বপূর্ণ যে "স্ব-রেফারেন্টিয়াল" এর অর্থ রেফারেন্স ব্যবহার করা আবশ্যক নয় । প্রকৃতপক্ষে, একটি স্ব-রেফারেন্টিয়াল কাঠামোর উদাহরণটি সুনির্দিষ্টভাবে বলেছে (জোর দেওয়া খনি):
আমরা এই সম্পর্কে কমলারটিকে সাধারণ রেফারেন্স সহ অবহিত করতে পারি না, যেহেতু সাধারণ theণ নেওয়ার নিয়ম দিয়ে এই ধরণটি বর্ণনা করা যায় না। পরিবর্তে আমরা একটি কাঁচা পয়েন্টার ব্যবহার করি , যদিও এটি একটি নাল নয় বলে পরিচিত, যেহেতু আমরা জানি যে এটি স্ট্রিংয়ের দিকে নির্দেশ করছে।
এই আচরণের জন্য একটি কাঁচা পয়েন্টার ব্যবহার করার ক্ষমতা মরিচা 1.0 থেকে বিদ্যমান। প্রকৃতপক্ষে, মালিকানা-রেফ এবং ভাড়া হুডের নীচে কাঁচা পয়েন্টার ব্যবহার।
Pin
টেবিলের সাথে যুক্ত হওয়া একমাত্র জিনিস হ'ল একটি সাধারণ উপায় যে প্রদত্ত মানটি স্থানান্তরিত না হওয়ার গ্যারান্টিযুক্ত।
আরো দেখুন:
Parent
এবংChild
সহায়তা করতে পারে ...