Owned Trait Objects

پیش‌تر دیدیم که چگونه می‌توان ازویژگی اشیاء (trait objects) با ارجاعات استفاده کرد، مثلاً dyn Pet&. با این حال، می‌توانیم از اشیاء ویژگی با اشاره‌گرهای هوشمند مانند Box نیز استفاده کنیم تا یک شیء ویژگی مالک (owned trait object) ایجاد کنیم: <Box<dyn Pet.

struct Dog {
    name: String,
    age: i8,
}
struct Cat {
    lives: i8,
}

trait Pet {
    fn talk(&self) -> String;
}

impl Pet for Dog {
    fn talk(&self) -> String {
        format!("  Woof، نام من {} است!", self.name)
    }
}

impl Pet for Cat {
    fn talk(&self) -> String {
        String::from("Miau!")
    }
}

fn main() {
    let pets: Vec<Box<dyn Pet>> = vec![
        Box::new(Cat { lives: 9 }),
        Box::new(Dog { name: String::from("Fido"), age: 5 }),
    ];
    for pet in pets {
        println!("سلام، شما کی هستید؟ {}", pet.talk());
    }
}

چیدمان حافظه پس از تخصیص pets:

<Dog as Pet>::talk<Cat as Pet>::talkStackHeapFidoptrlives9len2capacity2data:name,4,4age5vtablevtablepets: Vec<dyn Pet>data: CatDogProgram text
This slide should take about 10 minutes.
  • تایپ‌هایی که ویژگی معین را پیاده‌سازی می‌کنند ممکن است اندازه‌های مختلفی داشته باشند. این موضوع باعث می‌شود که داشتن مواردی مانند <Vec<dyn Pet در مثال بالا غیرممکن باشد.
  • dyn Pet راهی است برای اطلاع دادن به کامپایلر درباره یک تایپ با اندازه پویا که ویژگی Pet را پیاده‌سازی می‌کند.
  • در این مثال، pets در stack تخصیص داده می‌شود و داده‌های vector در heap هستند. دو عنصر vector اشاره‌گرهای چاق (fat pointers) هستند:
    • اشاره‌گر چاق (fat pointer) یک اشاره‌گر با عرض دو برابر است. این اشاره‌گر دو مؤلفه دارد: یک اشاره‌گر به شیء واقعی و یک اشاره‌گر به روش‌های جدول مجازی (vtable) برای پیاده‌سازی Pet آن شیء خاص.
    • داده‌های مربوط به Dog به نام Fido شامل فیلدهای name و age است. Cat دارای فیلد lives است.
  • خروجی‌های زیر را در مثال بالا مقایسه کنید:
    println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());
    println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());
    println!("{}", std::mem::size_of::<&dyn Pet>());
    println!("{}", std::mem::size_of::<Box<dyn Pet>>());