How to fix `self` escapes the method body here?

This is a part of code where I got this error (the error is marked with comment and located in the first task):

use std::sync::{Arc, Mutex as SyncMutex};
use async_channel::{Sender, Receiver};

pub struct UI<'a> {
    _event_flags: Arc<SyncMutex<UIEventFlags>>,
    _state_flags: Arc<SyncMutex<UIStateFlags>>,
    _characters_modal: Arc<SyncMutex<CharactersModal<'a>>>,
    _debug_panel: Arc<SyncMutex<DebugPanel<'a>>>,
    _mode_panel: Arc<SyncMutex<ModePanel>>,
    _realm_modal: Arc<SyncMutex<RealmModal<'a>>>,
    _title: Arc<SyncMutex<Title>>,
    _receiver: Receiver<HandlerOutput>,

impl<'a> Feature for UI<'a> {
    fn new(sender: Sender<HandlerOutput>, receiver: Receiver<HandlerOutput>) -> Self where Self: Sized {
        let component_options = UIComponentOptions {

        Self {
            _event_flags: Arc::new(SyncMutex::new(UIEventFlags::NONE)),
            _state_flags: Arc::new(SyncMutex::new(UIStateFlags::NONE)),
            _characters_modal: Arc::new(SyncMutex::new(CharactersModal::new(component_options.clone()))),
            _debug_panel: Arc::new(SyncMutex::new(DebugPanel::new(component_options.clone()))),
            _mode_panel: Arc::new(SyncMutex::new(ModePanel::new(component_options.clone()))),
            _realm_modal: Arc::new(SyncMutex::new(RealmModal::new(component_options.clone()))),
            _title: Arc::new(SyncMutex::new(Title::new(component_options))),
            _receiver: receiver,

    async fn run(&mut self) {
        execute!(std::io::stdout(), EnterAlternateScreen, EnableMouseCapture).unwrap();

        let mut terminal = Terminal::new(CrosstermBackend::new(std::io::stdout())).unwrap();

        let receiver = self._receiver.clone();

        let event_flags = Arc::clone(&self._event_flags);
        let state_flags = Arc::clone(&self._state_flags);

        let mut characters_modal = Arc::clone(&self._characters_modal);
        let mut debug_panel = Arc::clone(&self._debug_panel);
        let mut mode_panel = Arc::clone(&self._mode_panel);
        let mut realm_modal = Arc::clone(&self._realm_modal);
        let mut title = Arc::clone(&self._title);

            tokio::spawn(async move {
                let realm_modal = Arc::clone(&realm_modal);
                let characters_modal = Arc::clone(&characters_modal);
                let event_flags = Arc::clone(&event_flags);

                loop {
                    let event = read().unwrap();
                    if let Event::Key(key) = event {
                        let crossterm::event::KeyEvent { modifiers, code, .. } = key;

                        let mut event_flags = *event_flags.lock().unwrap();

                        event_flags.set(UIEventFlags::IS_EVENT_HANDLED, false);

                            .handle_key_event(modifiers, code, &mut event_flags).await;
                        mode_panel.lock().unwrap().handle_key_event(modifiers, code, &mut event_flags).await;
                        realm_modal.lock().unwrap().handle_key_event(modifiers, code, &mut event_flags).await;
                        // ERROR is here
                        debug_panel.lock().unwrap().handle_key_event(modifiers, code, &mut event_flags).await;

                        // ...

                    if let Event::Resize(_, _) = event {
            tokio::spawn(async move {
                let realm_modal = Arc::clone(&realm_modal);
                let characters_modal = Arc::clone(&characters_modal);

                loop {
                    if let Ok(output) = receiver.recv().await {
                        match output {
                            HandlerOutput::SuccessMessage(message, details) => {
                                debug_panel.lock().unwrap().add_item(LoggerOutput::Success(message, details));
                            HandlerOutput::ErrorMessage(message, details) => {
                                debug_panel.lock().unwrap().add_item(LoggerOutput::Error(message, details));
                            HandlerOutput::DebugMessage(message, details) => {
                                debug_panel.lock().unwrap().add_item(LoggerOutput::Debug(message, details));
                            HandlerOutput::IncomeMessage(message, details) => {
                                debug_panel.lock().unwrap().add_item(LoggerOutput::Income(message, details));
                            HandlerOutput::OutcomeMessage(message, details) => {
                                debug_panel.lock().unwrap().add_item(LoggerOutput::Outcome(message, details));
                            HandlerOutput::TransferCharactersList(characters) => {
                                event_flags.lock().unwrap().set(UIEventFlags::IS_CHARACTERS_MODAL_OPENED, true);
                            HandlerOutput::TransferRealmsList(realms) => {
                                event_flags.lock().unwrap().set(UIEventFlags::IS_REALM_MODAL_OPENED, true);
                            _ => {},
            // ....

The error is next:

error[E0521]: borrowed data escapes outside of method
   --> src/features/ui/
64  | impl<'a> Feature for UI<'a> {
    |      -- lifetime `'a` defined here
82  |     async fn run(&mut self) {
    |                  --------- `self` is a reference that is only valid in the method body
120 |                         debug_panel.lock().unwrap().handle_key_event(modifiers, code, &mut event_flags).await;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                         |
    |                         `self` escapes the method body here
    |                         argument requires that `'a` must outlive `'static`

Could somebody explain why this error happen and how to fix this ?

P.S. instead of mutable ref I also tried to pass Arc::clone(...), but result was the same.

You are capturing self by closure:

Make the lifetime of UI to be 'static.

You can also try to just remove the tokio::spawn. join_all works with futures, not necessarily needing spawned tokio tasks. Of course (with a multi-threaded run-time) this change would remove some parallelism. Instead, you could just create boxed futures (so they have the same type to fit into a vec), or use something like the join in futures - Rust macro that supports futures of different types.

On the other end of the spectrum of solutions/improvements/simplications, since this looks like not too many tasks are spawned anyways (this seems to be about having some “UI thread” more than about concurrently handling 1000s of network connections), you could also consider using ordinary threads, which have a nice scope API.

1 Like