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?
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.