Borrowed data escapes outside of my function

I get the following error which I don't understand and how to fix it

error[E0521]: borrowed data escapes outside of method
   --> src/websocket/websocket.rs:154:9
    |
64  |       fn started(&mut self, ctx: &mut Self::Context) {
    |                  ---------
    |                  |
    |                  `self` is a reference that is only valid in the method body
    |                  let's call the lifetime of this reference `'1`
...
154 | /         ctx.run_interval(DURATION, move |_, ctx| {
155 | |             let time = Local::now();
156 | |             let timestamp = time.timestamp();
157 | |             let req = handle_messages::get_req();
...   |
197 | |         });
    | |          ^
    | |          |
    | |__________`self` escapes the method body here
    |            argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.

for this function part

ctx.run_interval(DURATION, move |_, ctx| {
            let time = Local::now();
            let timestamp = time.timestamp();
            let req = handle_messages::get_req();

            if timestamp <= expired {

                match req.path() {
                    STUDENT_BLACKLIGHT => {
                        let data = send_message(BLACKLIGHT_TOPIC);
                        ctx.text(data.to_string());
                        reset_path()
                    },
                    MISSION_PATH => {
                        let data = send_message(MISSION_STATUS);
                        ctx.text(data.to_string());
                        if self.role.contains(&STUDENT_ROLE.to_string()) {
                            handle_messages::set_req(TEACHER_MISSION_NOTIFICATION.parse::<Uri>().unwrap());
                        } else {
                            reset_path()
                        }

                    },
                    LEARNING_UNIT_PATH => {
                        let data = send_message(LEARNING_UNIT_STATUS);
                        ctx.text(data.to_string());
                        if self.role.contains(&STUDENT_ROLE.to_string()) {
                            handle_messages::set_req(TEACHER_LU_NOTIFICATION.parse::<Uri>().unwrap());
                        } else {
                            reset_path();
                        }

                    },
                    _ => {}
                }


            } else {
                let msg = String::from("Connection closed");
                let data = Message::new(CONNECTION_TOPIC.to_string(), msg);
                ctx.text(data.to_string());
                ctx.stop()
            }
        });

what's the signature of Context::run_interval()?

guessing from the error message, it must have a 'static bound for the callback, which means you cannot capture any references in the closure. the error message seems to indicate you have captured self inside the closure, but I don't see it in the code you posted, maybe it's the ctx pamareter?

anyway, you should show the full signature of the run_interval() method, maybe there's more to the 'static bound?

What do you mean with full signature?

I think it is actix::AsyncContext::run_interval and it does indeed have a 'static bound on the callback.

So I have to set the lifetime by myself?

I pasted the wrong code snippet...

now it's clear. since the callback has a 'static bound, it cannot capture any references in it, specifically, it cannot capture self, which has the type &mut Self. instead, you should use first argument passed to it, to replace all the occurence of self within the closure, for example:

-ctx.run_interval(DURATION, move |_, ctx| {
+ctx.run_interval(DURATION, move |actor, ctx| {
 ...
-                       if self.role.contains(&STUDENT_ROLE.to_string()) {
+                       if actor.role.contains(&STUDENT_ROLE.to_string()) {
 ...
1 Like

It worked! I understand the fix but I don't understand the error itself.

which part? if I would guess, you may be lacking deeper understanding of closures.

when you create a closure, although the syntax looks very similar to defining a function, you are actually both defining a data type, and creating a value of the type, at the same time, and you also implement the Fn trait (and/or FnMut, FnOnce, based on the move keyword and code in the body of the "function") for the type. understanding the desugaring of a closure will help you understand the concept of "capture".

you can read the "closure" chapter of the rust book for details, but the short summary is, when you use self inside the closure, the closure must capture it, meaning it must store it as a field (remember a closure is a data type), because self is a reference type with a (non-'static) lifetime, the closure type must also have a lifetime, this violates the 'static bound required by run_interval().

also, I saw this recent post on lower level implementation details of closures, you may find it interesting too.

1 Like

Thanks for the explanation