Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
579 views
in Technique[技术] by (71.8m points)

generics - Unable to create a polymorphic type because the trait cannot be made into an object

I've got this simplified Rust code:

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>>,
}

The intention is that DnsRecord should be able to hold any struct implementing the DnsRecordData trait, with the different structs representing A, AAAA, CNAME etc.

This fails with the error:

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

What's got me the most confused is that by removing the generics from DnsRecordData::write(), it compiles just fine:

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>>,
}

If anyone can explain what I'm missing, I'd very much appreciate it.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The intention is that DnsRecord should be able to hold any struct implementing the DnsRecordData trait

That's not what the code says.

Vec<DnsRecord<dyn DnsRecordData>>

This is a vector of the struct DnsRecord containing the trait DnsRecordData. If you want "any struct implementing the DnsRecordData trait", you need a generic:

pub struct DnsPacket<D>
where
    D: DnsRecordData,
{
    pub answers: Vec<DnsRecord<D>>,
}

Traits can be implemented, but they also have their own type. In order to create this type, the trait needs to be object-safe - Trait Object is not Object-safe error.

As the error message states, this trait cannot be a trait object because there are generic types on the method.

The first error states that DnsRecord requires that whatever type it is parameterized with must implement DnsRecordData. However, the type of the trait object doesn't actually implement that. Normally, you'd use a trait object via a reference (&dyn DnsRecordData) or a box (Box<dyn DnsRecordData>), both of which should implement the trait, preventing this error.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...