Reference stored in Struct does not live long enough for closure - struct

I'm holding a reference to a Struct inside another Struct, both of which are declared in the same block.
I later want to use the outer Struct in a closure which is run repeatedly and never returns.
The reference inside the Struct apparently does not live long enough, but in my understanding, it should never go out of scope, or at least should live at least as long as the Struct it is referring to:
struct MyStruct;
struct ReferenceStruct<'a> {
reference: &'a MyStruct
}
impl<'a> ReferenceStruct<'a> {
fn do_something(&self) -> () {}
}
fn run<F>(mut function: F) -> !
where
F: FnMut() -> () + 'static
{
loop {
function();
}
}
fn main() {
let my_struct = MyStruct;
let reference = ReferenceStruct { reference: &my_struct };
run(move || {
reference.do_something();
});
}
(link to playground)
The run function (for context) mirrors an event loop, similar to that of Winit, and in reality, I have another Struct which owns the value being referenced, but this example reproduces it in fewer lines.
The error:
error[E0597]: `my_struct` does not live long enough
--> src\main.rs:26:50
|
26 | let reference = ReferenceStruct { reference: &my_struct };
| ^^^^^^^^^^ borrowed value does not live long enough
27 |
28 | / run(move ||
29 | | {
30 | | reference.do_something();
31 | | });
| |______- argument requires that `my_struct` is borrowed for `'static`
32 | }
| - `my_struct` dropped here while still borrowed
It appears that my_struct is dropped at the end of main, but even if the program flow somehow escapes the loop, it surely lasts as long as the reference struct, which is as long as it needs to. I don't understand where or how this error could come about, or what to do about it.

Your problem is the lifetime bound 'static for the closure passed into run(). This means that the lifetime of reference is also 'static because it is moved into the closure, which in turn means that my_struct must also have static lifetime – and that is not the case.
Luckily, you do not need the 'static bound here. If you remove it, everything works:
...
fn run<F>(mut function: F) -> !
where
F: FnMut() -> ()
...
However, this might not be a solution in your use case if the event loop needs the closure to be 'static.

Related

Moving values into closures via a stored reference

This code compiles:
use std::thread;
struct Foo;
fn use_foo(foo: &Foo) {}
fn main() {
let foo = Foo {};
thread::spawn(move || {
use_foo(&foo);
});
}
but this code does not:
use std::thread;
struct Foo;
struct Bar<'a> {
foo: &'a Foo,
}
fn use_bar(bar: Bar) {}
fn main() {
let foo = Foo {};
let bar = Bar { foo: &foo };
thread::spawn(move || {
use_bar(bar);
});
}
Instead failing with
error[E0597]: `foo` does not live long enough
--> src/main.rs:15:26
|
15 | let bar = Bar { foo: &foo };
| ^^^^ borrowed value does not live long enough
16 | / thread::spawn(move || {
17 | | use_bar(bar);
18 | | });
| |______- argument requires that `foo` is borrowed for `'static`
19 | }
| - `foo` dropped here while still borrowed
This bears some similarity to this issue, however here there is only a single reference to foo. Is there any way to get around this indirection introduced by bar and move foo into the closure?
Playground
There are a few things that prevent the second example from working:
The compiler will not move variables into the closure if they aren't used by the closure. The fact that foo is used through bar is not a factor.
You cannot move a value that you were only given a shared reference to. In the general sense, other references may exist and would be very put off by even mutating the value.
Even if you were to move the value foo, any references derived from it would be invalidated. The compiler prevents this from happening. This means even if you did use both foo and bar in the closure it still wouldn't work. You'd need to recreate bar with a fresh reference to the moved foo.
So in summary, no, you cannot in general take a structure that contains references and convert it or wrap it up into an owned version so that you can send it to another thread.
You can perhaps avoid references by using indexes (if appropriate for your real use-case) or perhaps you could use shared ownership (by using Arc instead of references). Or of course simply delay creating your referencing structure until after it has been moved to the thread.

Why rust told me that a reference still borrowed at the end of main function?

As you can see in the following code, I have two traits, one is called Hittable, and the other is called Material (I have been studying the book "ray-tracing-in-one-weekend", but use Rust).
The Hittable trait implements hit function for some objects (just like Sphere in this code), and every kind of objects includes its material (just like Glass, Wood...).
In my real project, the Sphere struct and another struct (called HitRecord in this book, used as mut reference to pass result in hit function), they both include &dyn Material, so that I need add lifetime parameter for both of them. However, to accomplish that, I should add lifetime parameter in the trait declaration, so I can assign the same lifetime parameter for Sphere and hit.
But the compiler indicates that the reference still under borrowed when the main function ends, I have no idea for that...
trait Hittable<'a> {
fn hit(&self);
}
trait Material {
fn result(&self);
}
struct Glass;
impl Material for Glass {
fn result(&self) {
println!("Glass is broken!");
}
}
struct Sphere<'a> {
name: String,
mat_ptr: &'a dyn Material,
}
impl<'a> Hittable<'a> for Sphere<'a> {
fn hit(&self) {
println!("Name is {}", self.name);
self.mat_ptr.result();
}
}
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a>>>,
}
fn main() {
let mut list = HT { objects: vec![] };
let surface_material = Glass;
let s = Sphere {
name: String::from("球"),
mat_ptr: &surface_material,
};
list.objects.push(Box::new(s));
}
the message shows
Compiling rust_test v0.1.0 (/home/hnyls2002/rust_test)
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `surface_material` is borrowed for `'static`
...
41 | }
| - `surface_material` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
This is because dyn Hittable<'a> is actually dyn Hittable<'a> + 'static, and thus s is required to live for 'static. The fix is to change HT to:
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a> + 'a>>,
}
Then you'll get a long but pretty self-explanatory error:
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
41 | }
| -
| |
| `surface_material` dropped here while still borrowed
| borrow might be used here, when `list` is dropped and runs the destructor for type `HT<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because surface_material is defined after list, it'll be dropped before and when the potential destructor for dyn Hittable will run it may access the freed surface_material. The fix is just to swap the declaration order of list and surface_material:
fn main() {
// always define before the container you are pushing into
let surface_material = Glass;
let mut list = HT { objects: vec![] };
// ...
}

How do I mutate a variable from inside a rust closure

I'm trying to implement an EventListener like interface in rust. I have a trait that takes a callback, the callback should mutate a variable from the scope it was defined in. But I get an error saying the borrowed value does not live long enough.
pub struct Target<T> {
funcs: Vec<Box<dyn FnMut(T) -> ()>>,
}
impl<T: Clone + 'static> Target<T> {
pub fn new() -> Target<T> {
return Target { funcs: Vec::new() };
}
pub fn add_listener(&mut self, func: Box<dyn FnMut(T) -> ()>) -> () {
self.funcs.push(Box::new(func));
}
pub fn trigger(&mut self, x: T) {
for callback in &mut self.funcs {
callback(x.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trigger_mutation() {
let mut a = 0;
let mut t: Target<i32> = Target::new();
t.add_listener(Box::new(|x: i32| {
println!("{}", x);
a = x;
}));
t.trigger(42);
let b = a.clone();
assert_eq!(b, 42);
}
}
I run it and get this:
$ cargo test -- --nocapture
Compiling luma-connector v0.1.0 (/home/blake/git/connector)
error[E0597]: `a` does not live long enough
--> src/target.rs:32:13
|
30 | t.add_listener(Box::new(|x: i32| {
| - -------- value captured here
| ________________________|
| |
31 | | println!("{}", x);
32 | | a = x + 1;
| | ^ borrowed value does not live long enough
33 | | }));
| |__________- cast requires that `a` is borrowed for `'static`
...
37 | }
| - `a` dropped here while still borrowed
But I get an error saying the borrowed value does not live long enough.
Well yes, your typing of Target implies nothing about scoping, so as far as the Rust typesystem is concerned, the closure could just fly into space unbouded by time (e.g. add_listener could pass it to a separate thread), therefore outlive trigger_mutation, which means a does not live long enough.
There are two ways to resolve this issue:
Use Arc/Rc with interior mutability (resp. Mutex and RefCell) in order to relax the lifetime of a: Arc version[0], Rc version, this is probably the simplest, and the least restrictive on Target, though it comes at a runtime cost.
Alternatively you can "scope" Target to tell Rust that it doesn't escape, therefore everything's perfectly kosher. I'm not sure it's the best way (hopefully somebody else can contribute that information) but bounding the FnMuts on a lifetime will allow rustc to reason about this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e67a4ab0faa8cc5d01c75293623c9fb4
This is basically free at runtime, but it means Target can't really escape its function.
So the former is probably what you want, the latter seems not super-useful for an events / notification system, but YMMV.
[0] an Atomic* would also work for the Arc version and be a bit easier & cheaper than a mutex, though it probably isn't very relevant for a test case.

How does one restrict a lifetime to a closure environment in rust?

I am calling closures with a fold function inside another closure. While I am intending for nothing in this some_closure function to live outside the closure environment, I am receiving an error that I am dropping a value while it is still borrowed.
I have tried removing all lifetime specifiers from some_closure, because I find the compiler is much smarter than myself at figuring out lifetimes, but I'm also finding no success in this (the compiler will always ask for lifetime specifiers leading up to the point of the shown example).
What I would desire to do here is to specify a lifetime restricted to the length of the closure inside the function, rather than the function itself. But I have a feeling that what I think is the problem may not actually be my problem, and that there is some gap in my understanding of lifetimes in closures.
I've tried to minimize the example as much as possible:
struct HoldStr<'a>(&'a str);
fn clone_slice_borrows_into_vec<'a>() -> impl Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
|slice| {
let mut temp = vec![];
temp.clone_from_slice(slice);
temp
}
}
fn clone_slice_borrows_into_vec_same<'a>() -> impl Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
// Same as last function for the sake of example, but one can assume it does something else
}
fn some_closure<'a>() -> impl Fn() {
|| {
let my_vec = vec![HoldStr("one"), HoldStr("two")];
let my_vec_holding_borrow: Vec<&'a HoldStr> = my_vec.iter().collect();
let called_closures: [Box<dyn Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>>>; 2] = [
Box::new(clone_slice_borrows_into_vec()),
Box::new(clone_slice_borrows_into_vec_same())
];
let _result = called_closures
.iter()
.fold(my_vec_holding_borrow, |acc, closure| closure(&acc));
}
}
I would expect everything to be dropped by the end of the closure inside some_closure and for this to be fine, especially since I am specifying that lifetime 'a does not relate to anything the function itself returns. But it seems that the borrowed value is expected to live until the end of the function itself. I get this error:
error[E0597]: `my_vec` does not live long enough
--> src/lib.rs:61:51
|
## | fn some_closure<'a>() -> impl Fn() {
| -- lifetime `'a` defined here
...
## | let my_vec_holding_borrow: Vec<&'a HoldStr> = my_vec.iter().collect();
| ---------------- ^^^^^^ borrowed value does not live long enough
| |
| type annotation requires that `my_vec` is borrowed for `'a`
...
## | }
| - `my_vec` dropped here while still borrowed
I'd be happy to hear anything from how to resolve the error, to that I've been going about this the wrong way in the first place.
You need higher-rank trait bounds for your closure types:
fn clone_slice_borrows_into_vec() -> impl for<'a> Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
...
(Full code in the playground)
The lifetime 'a isn't fixed for your closure. It should return a vector of references with lifetime 'a for any input slice with references of this lifetime. Your code used an externally fixed lifetime instead, which could be chosen by the caller of clone_slice_borrows_into_vec().
If you have a funciton definition like
fn foo<'a>() -> &'a Foo
then it's basically always a mistake. This lets the caller request an arbitrary lifetime, and the function promises to create a reference of this lifetime out of thin air, which is only possible if it gets static references from some global storage, in which case it should simply return &'static Foo.

Value doesn't live long enough when put in struct

I'm trying to work with LLVM in Rust using this crate. I'm trying to create a code generator struct to hold the context, module, and builder for me, but when I try to compile I get an error message that says c does not live long enough. How can I get this to compile, and why isn't c living long enough?
Code:
use llvm::*;
use llvm::Attribute::*;
pub struct CodeGen<'l> {
context: CBox<Context>,
builder: CSemiBox<'l, Builder>,
module: CSemiBox<'l, Module>,
}
impl<'l> CodeGen<'l> {
pub fn new() -> CodeGen<'l> {
let c = Context::new();
let b = Builder::new(&c);
let m = Module::new("test", &c);
CodeGen {
context: c,
builder: b,
module: m,
}
}
}
Full error message:
error: `c` does not live long enough
--> src/codegen.rs:17:31
|
17 | let b = Builder::new(&c);
| ^ does not live long enough
...
24 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
--> src/codegen.rs:15:33
|
15 | pub fn new() -> CodeGen<'l> {
| ^
error: `c` does not live long enough
--> src/codegen.rs:18:38
|
18 | let m = Module::new("test", &c);
| ^ does not live long enough
...
24 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
--> src/codegen.rs:15:33
|
15 | pub fn new() -> CodeGen<'l> {
| ^
error: aborting due to 2 previous errors
This looks like one of those situations where lifetime elision makes things less clear.
Here's the prototype of Builder::new:
pub fn new(context: &Context) -> CSemiBox<Builder>
Which might make you think that the CSemiBox doesn't have any relation to the lifetime of context. But the definition of CSemiBox has a lifetime parameter:
pub struct CSemiBox<'a, D>
As I understand it, when the output type of a function (in this case Builder::new) has a lifetime parameter, it can be elided if there is only one input lifetime. (The lifetime elision rules are described in the book and in this question.) In this case, the output lifetime is taken to be the same as the input lifetime. That means the prototype from before is actually equivalent to the following:
pub fn new<'a>(context: &'a Context) -> CSemiBox<'a, Builder>
I hope this clarifies what's happening: after Builder::new(&c), the CSemiBox contains a reference to the Context it was created from (b contains a reference to c). You can't put b and c in the same struct because the compiler has to be able to prove that c outlives b. For a more thorough explanation, see Why can't I store a value and a reference to that value in the same struct?
There are two ways I can think of to handle this. (You can't use Rc because you don't control the crate.)
Don't store the Context inside the CodeGen struct. You're limited in how you can structure your code, but that's not necessarily bad.
Since the Context is stored on the heap, you can use unsafe to make the references (appear to) have a 'static lifetime. Something like the following snippet ought to work, which removes the lifetime annotation from CodeGen. If you do this (as any time you use unsafe), you take responsibility for ensuring the safety of the exposed interface. That means, for example, CodeGen can't hand out references to builder and module, because that could leak a 'static reference to context.
pub struct CodeGen {
context: CBox<Context>,
builder: CSemiBox<'static, Builder>,
module: CSemiBox<'static, Module>,
}
impl CodeGen {
pub fn new() -> CodeGen {
let c = Context::new(); // returns a CBox<Context>
let c_static_ref: &'static _ = unsafe {
let c_ptr = c.as_ptr() as *const _; // get the underlying heap pointer
&*c_ptr
};
let b = Builder::new(c_static_ref);
let m = Module::new("test", c_static_ref);
CodeGen {
context: c,
builder: b,
module: m,
}
}
}

Resources