dyn Trait

علاوه بر استفاده از تریدها برای فراخوانی استاتیک از طریق generic‌ها، Rust همچنین از استفاده از آن‌ها برای فراخوانی داینامیک با تایپ‌های حذف‌شده از طریق اشیاء trait پشتیبانی می‌کند:

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!")
    }
}

// Uses generics and static dispatch.
fn generic(pet: &impl Pet) {
    println!("سلام، شما کی هستید؟ {}", pet.talk());
}

// Uses type-erasure and dynamic dispatch.
fn dynamic(pet: &dyn Pet) {
    println!("سلام، شما کی هستید؟ {}", pet.talk());
}

fn main() {
    let cat = Cat { lives: 9 };
    let dog = Dog { name: String::from("Fido"), age: 5 };

    generic(&cat);
    generic(&dog);

    dynamic(&cat);
    dynamic(&dog);
}
This slide should take about 5 minutes.
  • Generic‌ها، از جمله impl Trait، از monomorphization برای ایجاد یک نمونه تخصصی از تابع برای هر تایپ مختلفی که با آن نمونه‌سازی شده استفاده می‌کنند. این بدان معناست که فراخوانی یک متد trait از درون یک تابع generic همچنان از فراخوانی استاتیک استفاده می‌کند، زیرا کامپایلر اطلاعات کامل تایپ را دارد و می‌تواند پیاده‌سازی trait مربوط به تایپ را مشخص کند.

  • زمانی که از dyn Trait استفاده می‌شود، به‌جای آن از فراخوانی داینامیک از طریق یک virtual method table (vtable) استفاده می‌کند. این بدان معناست که یک نسخه واحد از fn dynamic وجود دارد که بدون توجه به تایپ Pet که وارد می‌شود، استفاده می‌شود.

  • زمانی که از dyn Trait استفاده می‌شود، شی trait باید پشت یک تایپ واسط قرار داشته باشد. در این مورد، این تایپ واسط یک ارجاع است، اگرچه تایپ‌های اشاره‌گرهای هوشمند مانند Box نیز می‌توانند استفاده شوند (این موضوع در روز سوم نشان داده خواهد شد).

  • در زمان اجرا، یک dyn Pet& به‌صورت یک "اشاره‌گر چاق" (fat pointer) نمایان می‌شود، یعنی یک جفت از دو اشاره‌گر: یکی از اشاره‌گرها به شیء مشخصی که Pet را پیاده‌سازی می‌کند اشاره دارد و دیگری به vtable برای پیاده‌سازی ترید آن نوع اشاره می‌کند. هنگام فراخوانی متد talk بر روی dyn Pet&، کامپایلر آدرس تابع talk را در vtable جستجو کرده و سپس تابع را فراخوانی می‌کند و اشاره‌گر به Dog یا Cat را به آن تابع پاس می‌دهد. کامپایلر نیازی به دانستن تایپ مشخص Pet برای انجام این کار ندارد.

  • یک dyn Trait به‌عنوان "تایپ ‌حذف‌ شده" (type-erased) در نظر گرفته می‌شود، زیرا دیگر در زمان کامپایل اطلاعاتی درباره تایپ مشخص نداریم.