[英]How to resolve 'Vec<Peekable<dyn Iterator<Item = T>>> doesn't have a size known at compile-time'?
[英]How can I make `Box<dyn Iterator>` peekable and avoid lifetime errors?
我有以下类型定义:
pub struct UTF8Chars {
bytes: Peekable<Box<dyn Iterator<Item = u8>>>,
}
现在我想知道如何实际创建这个结构的实例。
我试过了(是的,如果这是一个重要的细节,这是在特征实现中) :
impl<'a> ToUTF8Chars for &'a str {
fn utf8_chars(self) -> UTF8Chars {
let bytes = Box::new(self.bytes()).peekable();
UTF8Chars { bytes }
}
}
这给了我错误:
expected struct `Peekable<Box<(dyn Iterator<Item = u8> + 'static)>>`
found struct `Peekable<Box<std::str::Bytes<'_>>>`
如果我尝试了奇怪的事情,请原谅我,但我还没有掌握这种复杂的特质。 据我所知,rust-analyzer 告诉我Bytes
实际上是一个impl Iterator<Item = u8>
。 所以,接下来我尝试的是先投射它:
let bytes = Box::new(self.bytes()) as Box<dyn Iterator<Item = u8>>;
UTF8Chars { bytes: bytes.peekable() }
那种工作,但现在借用检查员抱怨:
impl<'a> ToUTF8Chars for &'a str {
-- lifetime `'a` defined here
fn utf8_chars(self) -> UTF8Chars {
let bytes = Box::new(self.bytes()) as Box<dyn Iterator<Item = u8>>;
^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static`
我不确定这里的 scope 会发生什么……据我所知,我拥有.bytes()
的结果(我还尝试使用额外的.clone()
以防假设不正确),我拥有Box
, Box
传递给Peekable
,最后Peekable
传递给UTF8Chars
。 这里的问题到底是什么? 为什么我需要比static
活得更久……?
我发现这个问题看起来很相似,遗憾的是没有答案: Peekable of an Iterator in struct 。
好吧,主要是因为我不太关心,或者无法关心底层数据到底是什么。 我只需要知道我可以.peek()
和.next()
等。这是因为有时我想为self.bytes
分配不同的东西。 例如, Chain<...>
或Copied<...>
而不是简单的vec::IntoIter<...>
。
如果有替代方法,我很高兴听到它。
所以,接下来我尝试的是先投射它:
let bytes = Box::new(self.bytes()) as Box<dyn Iterator<Item = u8>>;
在这种情况下这是正确的做法,尽管我会在let
上使用类型注释来编写它,而不是as
。
let bytes: Box<dyn Iterator<Item = u8>> = Box::new(self.bytes());
特别是,必须有一个点,从Box<Bytes>
到Box<dyn Iterator<Item = u8>>
的缩小强制发生,并且那个点必须在Box
包含在其他东西之前(因为它实际上产生了一个不同的Box
,其中一个添加了 vtable 指针)。
在某些情况下,就像as _
(未指定类型)足以提示编译器不会立即断定该类型与传入类型相同。
我不确定这里的 scope 会发生什么......
每个特征 object ( dyn
) 类型都有生命周期,通常是隐式的。 此生命周期指定该类型的实例保证多长时间有效——或者,从相反的角度来看,允许借用/包含哪些引用特征 object。
当您未指定该生命周期,并且特征 object 在Box
中时, 生命周期省略规则使该生命周期为'static
。 这就是您出错的原因:您试图将Bytes<'a>
放在需要'static
的地方。
为了允许您的盒装迭代器借用,您必须定义类型和特征以具有生命周期。
use core::iter::Peekable;
pub struct UTF8Chars<'a> {
bytes: Peekable<Box<dyn Iterator<Item = u8> + 'a>>,
}
trait ToUTF8Chars<'a> {
fn utf8_chars(self) -> UTF8Chars<'a>;
}
impl<'a> ToUTF8Chars<'a> for &'a str {
fn utf8_chars(self) -> UTF8Chars<'a> {
let bytes: Box<dyn Iterator<Item = u8> + 'a> = Box::new(self.bytes());
UTF8Chars {
bytes: bytes.peekable(),
}
}
}
如果您不想添加生命周期,那么您只能使用拥有迭代器(例如String::into_bytes(s).into_iter()
)。 在生命周期中,您可以使用拥有和借用迭代器。
问题是dyn Trait
类型实际上默认是dyn Trait + 'static
,这意味着它们不允许借用任何数据。 这对您来说是个问题,因为通过在&'a str
上调用bytes()
返回的迭代器从该str
借用,因此不能比'a
长。 但是'a
不会比'static
长寿,所以你不能从中创建一个dyn Iterator + 'static
。
正如您可能已经猜到的,这里的解决方案是添加一些更通用的生命周期界限,首先是结构:
pub struct UTF8Chars<'a> {
// ^^^^ now generic over 'a
bytes: Peekable<Box<dyn Iterator<Item = u8> + 'a>>,
// ------------------------^^^^
// the iterator is now allowed to borrow data for 'a
}
然后是特征:
trait ToUTF8Chars {
fn utf8_chars<'a>(self) -> UTF8Chars<'a> where Self: 'a;
// ^^^^ also generic over 'a ^^^^^^^^ self can borrow data for 'a
}
不过,根据您的具体用例,使用借来的接收器可能会更好:
trait ToUTF8Chars {
fn utf8_chars<'a>(&'a self) -> UTF8Chars<'a>;
// ^^^^ just borrow `self` for 'a
}
我敢肯定,在某些情况下,这两者并不相同(可能是对象安全?),但我无法确定具体情况。
最后是实施:
impl<'b> ToUTF8Chars for &'b str {
fn utf8_chars<'a>(self) -> UTF8Chars<'a> where Self: 'a {
let bytes = Box::new(self.bytes()) as Box<dyn Iterator<Item = u8>>;
UTF8Chars { bytes: bytes.peekable() }
}
}
或者
impl ToUTF8Chars for str {
fn utf8_chars<'a>(&'a str) -> UTF8Chars<'a> {
let bytes = Box::new(self.bytes()) as Box<dyn Iterator<Item = u8>>;
UTF8Chars { bytes: bytes.peekable() }
}
}
对于ToUTF8Chars
的替代版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.