Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Владеющие объекты трейтов

Ранее мы видели, как объекты трейтов можно использовать со ссылками, например &dyn Pet. Однако объекты трейтов можно использовать и с умными указателями, такими как Box, чтобы создать владеющий объект трейта: Box<dyn Pet>.

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

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, my name is {}!", 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!("Hello, who are you? {}", pet.talk());
    }
}

Раскладка памяти после выделения pets:

<Dog as Pet>::talk<Cat as Pet>::talkStackHeapFidoptrlives9len2capacity2data:name,4,4age5vtablevtablepets: Vec<Box<dyn Pet>>data: CatDogProgram text
This slide should take about 10 minutes.
  • Типы, которые реализуют данный трейт, могут иметь разные размеры. Поэтому, как в примере выше, невозможно иметь что-то вроде Vec<dyn Pet>.
  • dyn Pet — способ сообщить компилятору о типе динамического размера, который реализует Pet.
  • В примере pets выделен на стеке, а данные вектора — в куче. Два элемента вектора — толстые указатели (fat pointers):
    • Толстый указатель — это указатель двойной ширины. Он состоит из двух компонентов: указателя на сам объект и указателя на таблицу виртуальных методов (vtable) для реализации Pet этого конкретного объекта.
    • Данные для Dog по имени Fido — это поля name и age. У Cat есть поле lives.
  • Сравните эти выводы в примере выше:
    // Copyright 2024 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    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>>());