无法创建多态类型,因为无法将特征制作为对象

时间:2016-09-09 13:24:45

标签: generics rust

我有这个简化的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<DnsRecordData>>,
}

fn main() {}

意图是DnsRecord应该能够容纳任何实现DnsRecordData特征的结构,其中不同的结构代表A,AAAA,CNAME等。

失败并显示错误:

error: the trait bound `DnsRecordData + 'static: DnsRecordData` is not satisfied [--explain E0277]
  --> src/main.rs:14:5
   |>
14 |>     pub answers: Vec<DnsRecord<DnsRecordData>>,
   |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by `DnsRecord`

error: the trait `DnsRecordData` cannot be made into an object [--explain E0038]
  --> src/main.rs:14:5
   |>
14 |>     pub answers: Vec<DnsRecord<DnsRecordData>>,
   |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: method `write` has generic type parameters

让我最困惑的是,通过从DnsRecordData::write()删除泛型,它编译得很好:

use std::io::Result;

pub trait PacketBuffer {}

pub trait DnsRecordData {
    fn write(&self, buffer: &mut PacketBuffer) -> Result<usize>;
}

pub struct DnsRecord<R: DnsRecordData + ?Sized> {
    pub data: Box<R>,
}

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

fn main() {}

如果有人能解释我所缺少的东西,我会非常感激。

2 个答案:

答案 0 :(得分:10)

  

意图是parse应该能够容纳任何实现DnsRecord特征的结构

这不是代码所说的。

DnsRecordData

这是包含特征 Vec<DnsRecord<DnsRecordData>> 的结构DnsRecord的向量。如果你想要&#34;任何实现DnsRecordData特征&#34;的结构,你需要一个通用:

DnsRecordData

特征可以实施,但它们也有自己的类型。要创建此类型,特征需要对象安全 - Trait Object is not Object-safe error

正如错误消息所述,此特征不能是特征对象,因为该方法上存在泛型类型。

第一个错误表明pub struct DnsPacket<D> where D: DnsRecordData { pub answers: Vec<DnsRecord<D>>, } 要求参数化的任何类型都必须实现DnsRecord。但是,特征对象的类型并没有实际实现。通常,您通过引用(DnsRecordData)或框(&DnsRecordData)使用特征对象,这两个特征都应该实现特征,从而防止出现此错误。

答案 1 :(得分:8)

第二个错误来自于您无法为DnsRecordData创建特征对象,因为该特征不是&#34;对象安全&#34;。 trait objects section of The Rust Programming Language中解释了这个概念。

在您的特定情况下,特征包含一般方法。要创建特征对象,编译器必须合成特征的vtable,其中包含特征所具有的每个方法的函数指针。但是因为特征具有通用方法,所以它实际上具有与可以实例化的方法一样多的方法,这可能是无限的。因此,您无法为DnsRecordData创建特征对象。