توی یکی دیگه از مقالات راست میخوایم بخش هفت رو با هم بخوونیم.
توی بخش هفت میخوایم با مفهوم ownership
آشنا بشیم و قوانین های این موضوع رو بررسی کنیم و مثال هایی رو کار کنیم.
زبان راست مثل بعضی از زبان های دیگه دارای ویژگی ownership
هست.
اما سوالی که پیش میاد اینه که ownership
دقیقا توی راست چیه ؟
منظور از اسکوپ {} هست
سیستم ownership
توی راست به این صورت هست که تمام مقادیری که توی راست تعریف میشه باید یه صاحب داشته باشن توجه کنید که این صاحب باید تک و بی نظیر باشه یا به قولی یونیک باشه و چیزی مثل اون نباشه و باید گفت که مقادیری که توی یه اسکوپ مورد نظر هستن همان اسکوپی هست که صاحب توی آن اسکوپ هست.
مقادیر موجود توی راست میتونن پاس داده بشن به عنوان یک مقدار تغییرناپذیر با استفاده از &T
ولی شما میتونید مقادیر موجود توی راست رو پاس بدید به صورت تغییرپذیر در صورت نیاز به تغییر
و با استفاده از &mut T
این کار انجام میشه یا حتی میتونید از رفرنس استفاده نکنید و به این صورت T
این کارو انجام بدید، راست همیشه میتونه چند تا رفرنس تغییرناپذیر رو پاس بده یا فقط یک تغییرپذیر رو توجه کنید که بیشتر از یک رفرنس تغییرپذیر رو پاس بدیم با اررور مواجه میشیم که توی مقاله ی بعدی با هم بررسی میکنیم ، سیستم ownership
در کامپایلر راست یک اجبار هست و اختیاری نیست و حتی قوانین ownership
در زمان کامپایل چک میشه و حتی چک میکنه که تمام رفرنس ها مشکلی نداشته باشن که بعدا برای حل اینجور مشکلات هم راه حلی هست که لایف تایم
نام داره در مقاله ی بعدی هم با مفهوم رفرنس و بارویینگ آشنا میشیم.
ممکنه با خووندن توضیحات بالا کمی گیج شده باشید یا ممکنه گیج نشده باشید اما حالا بیاید قوانین ownership
رو بررسی کنیم و بعد انقدر مثال میزنیم که مسائل جا میوفته.
قوانین ownership
در راست در کل سه تا هست سعی کنید این قوانین رو حفظ کنید که در کار با راست خیلی به شما کمک میکنه و میتونیم بگیم ..که یک اجباره وگرنه به مشکل میخورید با راست.
- هر مقداری که توی راست تعریف میشه یه متغییر داره که صاحب یا owner نام داره
- هر مقداری در راست فقط یه صاحب میتونه داشته باشه
- زمانی که صاحبی که تعریف شده بود توی اسکوپ مشخصی از اون اسکوپ زد بیرون مقدار و صاحب کلا پاک میشه از حافظه
حالا بریم چند تا مثال حل کنیم تا مسائل بهتر جا بیوفته.
به مثال زیر توجه کنید
fn main() {
{
let x = String::from("Hello World"); // از اینجا به بعد متغییر x در دسترس هست
println!("{}", x); // هرکاری که میخواید رو با متغیر x انجام میدید
} // اینجا اسکوپی که ایجاد کرده بودیم تموم میشه و متغیر x از بین میره و دیگه در دسترس نیست
}
خروجی کد بالا
Hello World
در کد بالا یه مقداری رو توی راست تعریف کردیم در واقع متغییری رو ساختیم که مقداری رو داشت و صاحب اون مقدار x
بود و بعد کار هایی که میخواستیم رو با x
انجام دادیم و بعد از تموم شدن اسکوپ ایجاد شده دیگه x
از بین میره و دیگه در دسترس نیست.
شاید از خودتون بپرسید از کجا معلوم از بین میره ؟ خب جواب سادس میایم امتحان میکنیم.
به کد زیر توجه کنید
fn main() {
{
let x = String::from("Hello World");
}
println!("{}", x); // Error
}
خروجی کد بالا
error[E0425]: cannot find value `x` in this scope
println!("{}", x);
^ not found in this scop
دیدید که اثبات کردیم که قوانین ownership
در راست به صورت اجباری اجرا میشن و یک ویژگی اختیاری نیست.
حالا بیاید یه مثال دیگه بزنیم، به مثال زیر توجه کنید
fn main() {
let string1 = String::from("Hello World from sami2020pro");
let string2 = string1;
println!("{}", string2);
}
به خروجی کد بالا توجه باشید
Hello World from sami2020pro
اگه توجه کنید میبینید که string1
رو کپی کردیم توی string2
و اونو چاپ کردیم اما باید بگم که اشتباه فکر میکنید و حالا از خودتون میپرسید چرا ؟
در واقع ما string1
رو کپی نکردیم و در اصل انتقال دادیم به string2
و بعد از انتقال دیگه string1
وجود نداره و دیگه هم صاحب اون رشته نیست و در اصل string2
صاحب اون رشته شده.
شاید بگید این موضوع توی زبان پایتون وجود نداره ! بله درسته این موضوع توی پایتون وجود نداره ولی راست بسیار متفاوت از پایتون هست.
برای اثبات به مثال زیر توجه کنید
fn main() {
let string1 = String::from("Hello World from sami2020pro");
let string2 = string1;
println!("{}", string2);
println!("{}", string1); // Error
}
خروجی کد بالا
error[E0382]: borrow of moved value: `string1`
...
میبینید که ما string1
رو انتقال دادیم یا همون move
کردیم و دیگه نیست.
مفهوم ownership و توابعالبته این یه توضیح خیلی ساده هست و میتونید توضیحات بیشتری رو از مستندات اصلی پیدا کنید
زبان راست یک زبان فانکشنال هست و ما باید توی برنامه نویسی فانکشنال بهتر بهتر بشیم.
مفهوم ownership
برای فانکشن ها هم صدق میکنه.
پاس دادن یه مقداری به یه فانکشن مثل move
کردن میمونه که در بالاتر یادگرفتیم اما یه سری مسائل دیگه هم هست که باید یادبگیریم.
به مثال زیر توجه کنید
fn main() {
let s = String::from("Hello World"); // متغییر اس وارد اسکوپ میشه و از اینجا به بعد در دسترس هست
takes_ownership(s); // متغییر اس انتقال داده شده به این فانکشن و دیگه در دسترس نیست
let x = 5; // متغییر ایکس وارد اسکوپ میشه و از اینجا به بعد در دسترس هست
makes_copy(x); // متغییر ایکس میبایست به این فانکشن انتقال داده میشد اما خوده تایپ `i32` کپی هست
// و هنوز ایکس در دسترس هست
// در ادامه کپی رو توضیح خواهیم داد
} // در اینجا ایکس بیرون میره و بعد اس هم از اسکوپ بیرون میره ولی اس انتقال داده شده و اتفاقی نمیوفته
fn takes_ownership(some_string: String) { // اینجا متغییرمون وارد این اسکوپ شده
println!("{}", some_string);
} // اینجا متغییرمون از اسکوپ خارج شده و `drop` صدا زده شده و از حافظه یا همون مموری متغییرمون رو حذف میکنه
fn makes_copy(some_integer: i32) { // اینجا متغییرمون وارد این اسکوپ شده
println!("{}", some_integer);
} // اینجا متغییرمون از اسکوپ خارج میشه ولی اتفاق خاصی نمیوفته
خروجی کد بالا
Hello World
5
اگر کامنت های کد بالا رو بخوونید متوجه میشید که متغییر s
انتقال داده شده ولی متغییر x
همچین اتفاقی براش نیوفتاده و اینجور موارد کپی در راست وجود داره و در واقع متغییر s
که وجود داره ownership
رو به یکی دیگه داده ولی x
نه.
حالا بیاید اثبات کنیم که متغییر s
انتقال داده شده، به مثال زیر توجه کنید
fn main() {
let s = String::from("Hello World");
take_ownership(s);
println!("{}", s); // Error
}
fn take_ownership(some_string: String) {
println!("{}", some_string);
}
خروجی کد بالا
error[E0382]: borrow of moved value: `s`
...
دیدید که در بالا اثبات کردیم متغییر s
انتقال داده شده و دیگه در دسترس نیست.
اما حالا میخوایم ownership
رو از فانکشن ها بدیم یا بگیریم و بدیم اما شاید براتون سوال پیش بیاد که یعنی چی ؟
به مثال زیر توجه کنید
fn main() {
let s1 = gives_ownership(); // این فانکشن آنرشیپ رو به متغییرمون میده
let s2 = String::from("hello"); // در اینجا مقداری رو به متغییری دادیم در واقع یه متغییر تعریف کردیم
let s3 = takes_and_gives_back(s2); // اینجا آنرشیپ رو گرفتیم از متغییر `اس تو` و به متغییر `اس تری` دادیم
}
// این فانکشن آنرشیپی رو به کسی میده
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string
}
// این فانکشن آنرشیپ رو از کسی میگیره و به کسه دیگه ای میده
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
اگه کامنت های کد بالا رو خوونده باشید متوجه شدید که چطور میتونید آنرشیپی رو بدید یا از کسی بگیرید و به کسه دیگه ای بدید در مقاله ی بعدی ادامه ی مفهوم این مقاله رو درس میدیم.
امیدوارم از این مقاله هم لذت برده باشید.