繁体   English   中英

我怎样才能制作`Box<dyn iterator> ` 可窥视并避免终身错误?</dyn>

[英]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()以防假设不正确),我拥有BoxBox传递给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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM