Невозможно создать полиморфный тип, потому что черта не может быть превращена в объект
У меня есть этот упрощенный код Rust:
use std::io::Result;
pub trait PacketBuffer {}
pub trait DnsRecordData {
fn write<T: PacketBuffer>(&self, buffer: &mut T) -> Result<usize>;
}
pub struct DnsRecord<R: DnsRecordData + ?Sized> {
pub data: Box<R>,
}
pub struct DnsPacket {
pub answers: Vec<DnsRecord<dyn DnsRecordData>>,
}
Предполагается, что DnsRecord
сможет содержать любую структуру, реализующую черту DnsRecordData
, с различными структурами, представляющими A, AAAA, CNAME и т.д.
Это приводит к ошибке:
error[E0038]: the trait 'DnsRecordData' cannot be made into an object
--> src/lib.rs:14:5
|
14 | pub answers: Vec<DnsRecord<dyn DnsRecordData>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait 'DnsRecordData' cannot be made into an object
|
= note: method 'write' has generic type parameters
Что меня больше всего смутило, так это то, что, удаляя обобщенные элементы из DnsRecordData::write()
, он прекрасно компилируется:
use std::io::Result;
pub trait PacketBuffer {}
pub trait DnsRecordData {
fn write(&self, buffer: &mut dyn PacketBuffer) -> Result<usize>;
}
pub struct DnsRecord<R: DnsRecordData + ?Sized> {
pub data: Box<R>,
}
pub struct DnsPacket {
pub answers: Vec<DnsRecord<dyn DnsRecordData>>,
}
Если кто-нибудь сможет объяснить, что мне не хватает, я буду очень признателен.
Ответы
Ответ 1
Предполагается, что DnsRecord
сможет содержать любую структуру, реализующую черту DnsRecordData
Это не то, что написано в коде.
Vec<DnsRecord<dyn DnsRecordData>>
Это вектор структуры DnsRecord
, содержащей признак DnsRecordData
. Если вам нужна "любая структура, реализующая черту DnsRecordData
", вам нужен универсальный:
pub struct DnsPacket<D>
where
D: DnsRecordData,
{
pub answers: Vec<DnsRecord<D>>,
}
Черты могут быть реализованы, но они также имеют свой собственный тип. Чтобы создать этот тип, черта должна быть объектно-безопасной - Объект черты не является объектно-безопасной ошибкой.
Как говорится в сообщении об ошибке, эта черта не может быть объектом черты, поскольку в методе есть универсальные типы.
Первая ошибка гласит, что DnsRecord
требует, чтобы любой тип, с которым он параметризован, реализовывал DnsRecordData
. Однако тип объекта черты фактически не реализует это. Обычно вы используете объект признака через ссылку (&dyn DnsRecordData
) или блок (Box<dyn DnsRecordData>
), оба из которых должны реализовывать признак, предотвращая эту ошибку.
Ответ 2
Ошибка возникает из-за того, что вы не можете создавать объекты черт для DnsRecordData
из-за того, что черта не "объектобезопасна". Эта концепция объясняется в разделе объектов черты языка программирования Rust.
В вашем конкретном случае, черта содержит общий метод. Чтобы создать объект признака, компилятор должен синтезировать виртуальную таблицу для признака, содержащую указатель функции для каждого метода, который имеет признак. Но так как у признака есть универсальный метод, он эффективно имеет столько методов, сколько может быть создано для метода, что потенциально бесконечно. Следовательно, вы не можете создать объект черты для DnsRecordData
.