Что происходит, когда я передаю конкретную ссылку на структуру функции, которая принимает типы объектов?

В следующем коде у меня есть простой трейт A и структура Foo, которая реализует A...

Затем я определяю функцию, которая принимает ссылку на трейт-объект. Из main() я передаю ссылку на конкретный Foo. Это отлично работает и успешно вызывает метод в трейте.

trait A {
    fn do_something(&self) {
        println!("Doing something.");
    }
}

struct Foo {}
impl A for Foo {}

fn main() {
    let foo = Foo {};
    make_do_something(&foo);
}

fn make_do_something(a: &dyn A) {
    a.do_something();
}

Что здесь делает компилятор Rust? Конкретная ссылка Foo автоматически приводится к ссылке на объект Trait? То есть создается ли в куче новый трейт-объект, который ссылается на мой конкретный объект? Я не могу найти четкого описания того, как это работает в документации.

Да, они автоматически принуждаются. Это одна из форм «неразмерного принуждения». doc.rust-lang.org/reference/…

PitaJ 14.04.2023 00:01

Спасибо @PitaJ. Эта ссылка помогает. Возможно, этот вопрос здесь неуместен, но я не уверен на 100%, почему трейт-объекты не имеют размера. То есть я считаю, что трейт-объект всегда должен содержать ровно 2 указателя — указатель на vtable и указатель на конкретный объект. Поэтому я не уверен, почему вам всегда нужно &dyn Foo или Box<dyn Foo> вместо просто dyn Foo. Например, почему dyn Foo размером ровно 2 указателя не может жить прямо в стеке? Я уверен, что мне не хватает какой-то тонкости.

Chuck 14.04.2023 00:16

Трейт-объект dyn Foo сам по себе не является указателем, это просто тип (с динамическим размером и т. д.). Только в сочетании со ссылкой или интеллектуальным указателем (например, &dyn Foo или Box<dyn Foo>) он обрабатывается так, как вы описываете. &dyn Foo — это ровно два указателя, которые живут в стеке.

PitaJ 14.04.2023 00:50

Еще раз спасибо, @PitaJ. Ваш ответ и ответ ниже от Кевина Рида прояснили это для меня.

Chuck 14.04.2023 02:28
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
4
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Конкретная ссылка Foo автоматически приводится к ссылке на объект Trait?

Да, &Fooпринуждают к &dyn A. &Foo состоит из указателя на данные Foo (который в вашем примере имеет длину 0 байтов, но это не имеет значения); &dyn A состоит из этого указателя, а также указателя на vtable, созданную из impl A for Foo.

это новый трейт-объект, создаваемый в куче

Нет; трейт-объект требует дополнительных данных для ссылки на него, а не для его хранения.

Сравните это с другим основным типом динамического размера (!Sized) в Rust, срезом: &Foo содержит указатель на Foo, а &[Foo] содержит этот указатель и длину. Это могут быть одни и те же данные, на которые указывают (вы можете преобразовать в обе стороны), но указатель другой.

В целом можно сказать, что для указания на значение типа Sized требуется только машинный указатель на данные; указание на значение типа !Sized требует дополнительной информации (называемой «метаданными»).

Каждый указатель на тип с динамическим размером создается либо с помощью приведения, и в этом случае компилятор вставляет необходимые данные, известные во время компиляции (vtable для трейт-объекта или длину для указателя среза, полученного из массива указателей), или кодом библиотеки, который что-то знает о том, для чего он создает указатель (например, когда Vec<T> создает &[T]).

Другие вопросы по теме