使用盒装特质对象时,生锈的生命规则是什么?

发布于 2025-02-04 08:22:14 字数 764 浏览 2 评论 0原文

下面的代码不会与ERROR 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());
}

但是,如果我使用对特征对象的引用而不是盒装特质对象,它可以工作:

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

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

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

    _it = &two.chars();
}

另外,如果我不使用特质对象,它也可以工作:

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());
}

为什么?

The code below doesn't compile with error E0597: borrowed value (two) doesn't live long enough.

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());
}

But, if instead of boxed trait object I use reference to a trait object it works:

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

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

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

    _it = &two.chars();
}

Also, if I don't use trait object it works too:

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());
}

Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

葮薆情 2025-02-11 08:22:14

让我们一个一个一个。

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

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

,如说明所示,值以相反的顺序删除。由于两个在第6行中定义,_it在第4行中定义了(未分配的分配),编译器将尝试销毁两个在函数结束时试图销毁_it之前。但是到我们到达函数结束时,_IT保留对两个(第8行)的引用,而施加的订购将销毁两个两个 _IT仍然对其进行引用。 _IT 的攻击子可以观察销毁的值两个,因此不允许使用。

为什么甚至有攻击者?因为特征对象dyn Iterator ... - 就像任何其他特征对象一样 - 可以 都可以容纳具有击振子的动态类型。灾难可能会观察到它可能参考的事物。据编译器所知,这是两个。尽管实际chars -Type不会遭受此问题的困扰,但我可以召集这种类型并将其粘在dyn Iterator ...中。因此,使用性状对象,删除值的顺序始终很重要。

解决方案是更改定义顺序:

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&lt; u32&gt;)一样。因为它是一种具体的类型,所以编译器可以找出box内的类型(这是chars ,并保留某些寿命的引用),具有琐碎destructor(无用),因此box具有简单地交易的微不足道驱动器;因此,两个实际上在_IT之前被摧毁并不重要,因为_IT肯定不会观察到两个两个< /code>在其破坏者中。

Let's take them one by one.

In your first example, the problem is subtle and hinted at by the error message

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

As the note says, values are dropped in the opposite order they are defined. As two is defined in line 6 and _it is defined (not assigned) in line 4, the compiler will try to destroy two before it tries to destroy _it at the end of the function. But by the time we reach the end of the function, _it holds a reference to two (line 8), and the imposed ordering would destroy two while _it still holds a reference to it. The destructor of _it could observe the destroyed value two, so this is not allowed.

Why is there even a destructor? Because the trait object dyn Iterator... - just like any other trait object - could hold some dynamic type that has a destructor. And that destructor might observe things it potentially references; as far as the compiler knows, that's two. While the actual Chars-type does not suffer from this problem, I could conjure up such a type and stick it into a dyn Iterator.... So with trait objects, the order in which values are dropped is always important.

The solution is to change the order of definition:

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());
}

In the second example, you are using plain references. Since plain references do not have destructors, there is no destructor that could potentially observe a destroyed value while it is running, and the order in which values are destroyed is not important; so it compiles just fine.

In the third example, there are no trait objects involved, the boxed type is simply a normal type as far as the compiler is concerned (just like Box<u32>). Because it is a concrete type, the compiler can figure out that the type inside the Box (which is a Chars, and holds a reference of some lifetime), has a trivial destructor (which does nothing), and therefore the Box has a trivial destructor that simply deallocates; so it doesn't matter that two is in fact destroyed before _it, because _it will definitely not be able to observe two in its destructor.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文