繁体   English   中英

使用盒装特征对象时,Rust 中的生命周期规则是什么?

[英]What are the rules for lifetimes in Rust when using boxed trait objects?

下面的代码无法编译并出现错误 E0597:借用的值(二)寿命不够长。

fn main() {
    let one = String::from("one");

    let mut _it: Box<dyn Iterator<Item=char>> = Box::new(one.chars());

    let two = String::from("two");

    _it = Box::new(two.chars());
}

但是,如果我使用对特征 object 的引用而不是盒装特征 object 它会起作用:

fn main() {
    let one = String::from("one");

    let mut _it: &dyn Iterator<Item=char> = &one.chars();

    let two = String::from("two");

    _it = &two.chars();
}

另外,如果我不使用特征 object,它也可以工作:

fn main() {
    let one = String::from("one");

    let mut _it: Box<_> = Box::new(one.chars());

    let two = String::from("two");

    _it = Box::new(two.chars());
}

为什么?

让我们一一列举。

在您的第一个示例中,问题很微妙,并由错误消息提示

8 |     _it = Box::new(two.chars());
  |                    ^^^^^^^^^^^ borrowed value does not live long enough
9 | }
  | -
  | |
  | `two` dropped here while still borrowed
  | borrow might be used here, when `_it` is dropped and runs the destructor for type `Box<dyn Iterator<Item = char>>`
  |
  = note: values in a scope are dropped in the opposite order they are defined

正如注释所说,值的删除顺序与它们定义的顺序相反。 由于two在第 6 行定义, _it在第 4 行定义(未赋值),编译器会在尝试销毁_it末尾的 _it 之前先销毁two 。但是当我们到达 function 末尾时, _it持有对two的引用(第 8 行),强加的排序会破坏two_it仍然持有对它的引用。 _it的析构函数可以观察到被破坏的值two ,所以这是不允许的。

为什么还有析构函数? 因为特征 object dyn Iterator... - 就像任何其他特征 object -可以包含一些具有析构函数的动态类型。 那个析构函数可能会观察到它可能引用的东西; 据编译器所知,这是two . 虽然实际的Chars类型不会遇到这个问题,但我可以想出这样一种类型并将其粘贴到dyn Iterator...中。 因此对于 trait 对象,删除值的顺序总是很重要。

解决办法是改变定义的顺序:

fn main() {
    let one = String::from("one");

    let two; // Notice the definition, before `_it` so it gets dropped *after*

    let mut _it: Box<dyn Iterator<Item=char>> = Box::new(one.chars());

    two = String::from("two");

    _it = Box::new(two.chars());
}

在第二个示例中,您使用的是普通引用。 由于普通引用没有析构函数,因此没有析构函数可以在运行时潜在地观察到被销毁的值,并且销毁值的顺序并不重要; 所以它编译得很好。

在第三个示例中,不涉及特征对象,就编译器而言,装箱类型只是普通类型(就像Box<u32> )。 因为它是一个具体类型,所以编译器可以找出Box中的类型(它是一个Chars ,并持有某个生命周期的引用),有一个普通的析构函数(什么都不做),因此Box有一个普通的析构函数简单解除分配的析构函数; 所以two实际上在_it之前被销毁并不重要,因为_it肯定无法在其析构函数中观察到two

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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