مفاهیم جابه‌جایی

انتساب, مالکیت را بین متغیرها منتقل می‌کند:

fn main() {
    let s1: String = String::from("سلام!");
    let s2: String = s1;
    println!("s2: {s2}");
    // println!("s1: {s1}");
}
  • انتساب s1به s2 مالکیت را منتقل می‌کند.
  • زمانی که دیگر در اسکوپ s1 نیستیم, هیچ اتفاقی نمی‌افتد: چون s1 مالک چیزی نیست.
  • زمانی که دیگر در اسکوپ s2 نیستیم, داده‌های رشته آزاد می‌شوند.

قبل از انتقال به s2 :

StackHeaps1ptrHello!len6capacity6

بعد از انتقال به s2 :

StackHeaps1ptrHello!len6capacity6s2ptrlen6capacity6(inaccessible)

هنگامی که یک مقدار را به یک تابع منتقل می‌کنید، مقدار به آرگمان تابع اختصاص داده می‌شود. به این شکل مالکیت را منتقل می‌کند:

fn say_hello(name: String) {
    println!("سلام {name}")
}

fn main() {
    let name = String::from("الیس");
    say_hello(name);
    // say_hello(name);
}
This slide should take about 5 minutes.
  • اشاره کنید که این رویه راست بر خلاف پیش‌فرض زبان C++ است که در ان مقدار کپی میشود مگر که از std::move استفاده کنیم ( تا یک مقدار را جا به جا کنیم!)

  • این رویه فقط برای انتقال مالکیت است. اینکه آیا هیچ کد ماشینی برای دستکاری خود داده‌ها تولید می‌شود یا خیر، موضوعی برای بهینه‌سازی است و چنین کپی‌هایی به‌طور تهاجمی (aggressively) بهینه‌سازی می‌شوند.

  • مقادیر ساده (مانند اعداد صحیح) را می‌توان Copy کرد (اسلایدهای بعدی را ببینید).

  • در Rust، کلون‌ها واضح بیان می‌شوند (با استفاده از clone).

در مثال say_hello:

  • با اولین فراخوانی say_hello، تابع main مالکیت name را انتقال می‌دهد. پس از آن، name دیگر نمی‌تواند در main استفاده شود.
  • حافظه انباشت اختصاص داده شده برای name در انتهای تابع say_hello آزاد خواهد شد.
  • تابع main می‌تواند مالکیت name را حفظ کند اگر آن را به عنوان یک مرجع ( &name) منتقل کند و صد البته که say_hello یک مرجع را به عنوان پارامتر باید بپذیرد.
  • به عنوان گزینه دیگر، main می‌تواند یک کلون از name را در فراخوانی اولیه تابع ای که در نظر داریم ( name.clone()) منتقل کند.
  • در زبان Rust سخت‌تر از C++ است که سهوا کپی ایجاد کنیم, زیر به صورت پیش‌فرض از مفهوم «انتقال» استفاده میکنیم و برنامه نویس مجبور است هر جا که لازم هست به صورت صریح کلون را ایجاد کند.

برای کاوش بیشتر

نسخههای دفاعی در ++C مدرن

C++ مدرن این مشکل را به شیوه متفاوتی حل می‌کند:

std::string s1 = "Cpp";
std::string s2 = s1;  // Duplicate the data in s1.
  • داده‌های انباشت از داده‌های s1 یک کپی برابر اصل برای s2 گرفته می‌شود که این کپی به صورت مستقل است.
  • حالا هر موقع s1 یا s2 از اسکوپ موردنظرشون خارج شوند هر کدام به صورت جداگانه‌ای حافظه خود را آزاد میکنند.

قبل از انتساب همراه کپی:

StackHeaps1ptrCpplen3capacity3

بعد از انتساب همراه کپی:

StackHeaps1ptrCpplen3capacity3s2ptrCpplen3capacity3

نکات کلیدی:

  • زبان C++ انتخاب کمی متفاوت نسبت به زبان Rust انجام داده است. زیرا = داده‌ها را کپی می‌کند، داده‌های رشته باید کلون شوند. در غیر این صورت، هر موقع از اسکوپ یکی از آنها خارج شویم امکان به وجود آمدن اشتباه آزادسازی مجدد حافظه رخ دهد.

  • البته که زبان C++ دارای std::move است که برای انتقال یک متغییر استفاده میشود. اگر مثال ما s2 = std::move(s1) بود هیچ تخصیص انباشتی صورت نمیگرفت بلکه s1 در یک وضعیت معتبر البته نامشخص قرار میگرفت و برخلاف زبان Rust, توی زبان C++ برنامه‌نویس مجاز است که دوباره از s1 استفاده کند.

  • بر خلاف Rust، = در C++ می‌تواند برای کپی کردن و هم انتقال دادن استفاده شود.