<Box<T

Box یک اشاره‌گر مالک به داده‌های روی heap است:

fn main() {
    let five = Box::new(5);
    println!("five: {}", *five);
}
5StackHeapfive

<Box<T ویژگی <Deref<Target = T را پیاده‌سازی می‌کند، که به این معناست که می‌توانید مستقیم روش‌های T را روی <Box<T فراخوانی کنید.

تایپ‌های داده‌های بازگشتی یا انواع داده با اندازه‌های دینامیک را نمی‌توان به صورت inline بدون pointer indirection ذخیره کرد، که می‌توان با استفاده از Box آن کار را کرد:

#[derive(Debug)]
enum List<T> {
    /// A non-empty list: first element and the rest of the list.
    Element(T, Box<List<T>>),
    /// An empty list.
    Nil,
}

fn main() {
    let list: List<i32> =
        List::Element(1, Box::new(List::Element(2, Box::new(List::Nil))));
    println!("{list:?}");
}
StackHeaplistElement1Element2Nil
This slide should take about 8 minutes.
  • Box مانند std::unique_ptr در ++C است، با این تفاوت که تضمین شده است که هیچ‌گاه تهی (null) نخواهد بود.

  • Box می‌تواند زمانی مفید باشد که شما:

    • یک تایپ دارید که اندازه آن در زمان کامپایل مشخص نیست، اما کامپایلر Rust نیاز به دانستن اندازه دقیق آن دارد.
    • می‌خواهید مالکیت مقدار زیادی داده را انتقال دهید. برای جلوگیری از کپی کردن حجم زیادی از داده‌ها در پشته، به جای آن داده‌ها را در heap در یک Box ذخیره کنید تا فقط اشاره‌گر منتقل شود.
  • اگر از Box استفاده نمی‌کردیم و سعی می‌کردیم یک List را مستقیماً در داخل List قرار دهیم، کامپایلر نمی‌توانست اندازه ثابتی برای ساختار در حافظه محاسبه کند (زیرا List اندازه‌ای بی‌نهایت پیدا می‌کرد).

  • Box این مشکل را حل می‌کند زیرا اندازه‌ای برابر با یک اشاره‌گر عادی دارد و فقط به عنصر بعدی List در heap اشاره می‌کند.

  • Box را از تعریف List حذف کنید و خطای کامپایلر را نمایش دهید. پیام خطا "recursive without indirection" را دریافت خواهیم کرد، زیرا برای رکورسیون داده‌ها باید از یک روش غیرمستقیم، مانند Box یا ارجاعی از نوعی، به جای ذخیره مستقیم مقدار استفاده کنیم.

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

بهینه سازی Niche

اگرچه Box مشابه std::unique_ptr در ++C به نظر می‌رسد، اما نمی‌تواند خالی/null باشد. این ویژگی باعث می‌شود که Box یکی از تایپ‌هایی باشد که به کامپایلر اجازه می‌دهد ذخیره‌سازی برخی از enumها را بهینه‌سازی کند.

برای مثال، <<Option<Box<T همان اندازه را دارد که <Box<T، زیرا کامپایلر از مقدار NULL برای تمایز بین variant‌ ها به جای استفاده از تگ صریح استفاده می‌کند ("بهینه‌سازی اشاره‌گر خالی"):

use std::mem::size_of_val;

struct Item(String);

fn main() {
    let just_box: Box<Item> = Box::new(Item("Just box".into()));
    let optional_box: Option<Box<Item>> =
        Some(Box::new(Item("Optional box".into())));
    let none: Option<Box<Item>> = None;

    assert_eq!(size_of_val(&just_box), size_of_val(&optional_box));
    assert_eq!(size_of_val(&just_box), size_of_val(&none));

    println!("Size of just_box: {}", size_of_val(&just_box));
    println!("Size of optional_box: {}", size_of_val(&optional_box));
    println!("Size of none: {}", size_of_val(&none));
}