Can anyone help me? I can't use self in closure!

I can't use self in closure! The code is here.

use std::thread;

struct MyManager;

impl MyManager {
    pub fn run(&self) {

        // 1. receive message in handler
        let handler = |x: i32| {
            // 2. then execute time-consuming computing in another thread, or cause blocking

            // !!! FIXME:  
            // but the code below compile failed, please help me here.
            thread::spawn(move || {
                self.compute(x);
            });
            "ok".to_string()
        };


        self.init(Box::new(handler));

    }

    pub fn init(&self, handler: Box<dyn FnMut(i32) -> String>) {
        println!("initiallized ...");
    }

    pub fn compute(&self, x: i32) {
        println!("computing {}", x);
    }
}

#[test]
pub fn basic() {
    let manager = MyManager{};
    manager.run();
}

Then according to the compiler prompt,I modify code to the following.

use std::thread;

struct MyManager;

impl MyManager {
    pub fn run(&'static self) {

        // 1. receive message in handler
        let handler = |x: i32| {
            // 2. then execute time-consuming computing in another thread, or cause blocking

            // !!! FIXME:  
            // but the code below compile failed, 
            thread::spawn(move || {
                self.compute(x);
            });
            "ok".to_string()
        };


        self.init(Box::new(handler));

    }

    pub fn init(&self, handler: Box<dyn FnMut(i32) -> String>) {
        println!("initiallized ...");
    }

    pub fn compute(&self, x: i32) {
        println!("computing {}", x);
    }
}

#[test]
pub fn basic() {
    let manager = MyManager{};
    let manager = Box::leak(Box::new(manager));
}

But compile failed again and prompt the following error.

error[E0597]: `self` does not live long enough
  --> src\learn\mymanager.rs:15:17
   |
9  |         let handler = |x: i32| {
   |                       -------- value captured here
...
15 |                 self.compute(x);
   |                 ^^^^ borrowed value does not live long enough
...
21 |         self.init(Box::new(handler));
   |                   ----------------- cast requires that `self` is borrowed for `'static`
22 |
23 |     }
   |     - `self` dropped here while still borrowed

I don't know what to do and need some help please.

The compiler's 'static suggestion is notoriously misleading, and usually indicates that there's a more fundamental problem with your design. In this case, I suspect that you'd be better served by Arc<Self>, but it's hard to be sure without more details.

Can you provide a bit more context, like the full definition of struct MyManager and the function signatures of MyManager::init and MyManager::compute?

———

Edit: I missed the function definitions due to the webpage’s scrolling, sorry. The real definition of MyManager would still be helpful, though.

5 Likes

The root cause is: Thread::spawn requires 'static reference.

pub fn spawn<F, T>(f: F) -> JoinHandle<T>where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,

The 'static constraint means that the closure and its return value must have a lifetime of the whole program execution. The reason for this is that threads can outlive the lifetime they have been created in.

There are two possible solutions to solve it.

  1. Transfer MyManager's ownership to the thread, and you cannot use it in main thread anymore.
    pub fn run(self) {
        // 1. receive message in handler
        let handler = |x: i32| {
            // 2. then execute time-consuming computing in another thread, or cause blocking
            thread::spawn(move || {
                self.compute(x);
            });
            "ok".to_string()
        };
        // self.init(Box::new(handler));
    }
  1. Wrap it with Arc, which means put it into heap and share it between both threads.
use std::thread;
use std::sync::Arc;

struct MyManager;

impl MyManager {
    pub fn run(m: &Arc<Self>) {
        // 1. receive message in handler
        let handler ={
         let m1 = Arc::clone(m);   
            |x: i32| {
                // 2. then execute time-consuming computing in another thread, or cause blocking
                thread::spawn(move || {
                    m1.compute(x);
                });
                "ok".to_string()
            }
        };
        m.init(Box::new(handler))
    }

    pub fn init(&self, handler: Box<dyn FnOnce(i32) -> String>) {
        println!("initiallized ...");
    }

    pub fn compute(&self, x: i32) {
        println!("computing {}", x);
    }
}

#[test]
pub fn basic() {
    let manager = Arc::new(MyManager{});
    MyManager::run(&manager);
}

BTW. If you need to modify it at another thread, Mutex is required.

2 Likes

The minimal change to get your code to compile is to make the handler a move closure (in the second code example):

-         let handler = |x: i32| {
+         let handler = move |x: i32| {

There's a good chance, though, that you'll run into other problems down the line. If you can describe the big picture of what you're trying to do, someone may be able to suggest a better approach.


For example, depending on the requirements, I might write something like this:

use std::thread;

struct MyManager;

impl MyManager {
    pub fn run(&self) {
        let handler = |manager: &MyManager, x| {
            manager.spawn_compute(x);
            "ok".to_string()
        };

        self.init(handler);
    }

    pub fn init(&self, handler: impl FnMut(&Self, i32) -> String) {
        let _handler: Box<dyn FnMut(&Self, i32)->String> = Box::new(handler);
        println!("initiallized ...");
    }

    pub fn spawn_compute(&self, x: i32)->thread::JoinHandle<()> {
        thread::spawn(move || {
            println!("computing {}", x);
        })
    }
}

fn main() {
    let manager = MyManager{};
    manager.run();
}
3 Likes

You will never be able to use a &self in thread::spawn. This is because thread::spawn requires something that lives for indefinitely long time, longer than a single method call. And &self by definition gives you permission to use self only for duration of one call.

You can have fn run(self) method, without the temporary reference. This gives the method exclusive ownership of all of self, which it then can move to a thread.

fn run(self: Arc<Self>) is an option too if you want to be able to use the type both in the thread and outside of it. Arc allows sharing of a type.

You can also separate fields needed by the thread into another type:

struct MyManager {
   thread_stuff: Option<ThreadStuff>,
}

and use:

let thread_stuff = self.thread_stuff.take(); // this will leave `None` behind
thread::spawn(move || thread_stuff.work());
6 Likes

Others gave you lots of helpful suggestions, but they skipped the step zero: understanding of what you are, actually, trying to achieve here.

It looks as if you are trying to write some other language in Rust. I don't know whether you plan to write JavaScript in Rust, Python in Rust or some other language in Rust the advice is the same: don't do that (the article lists many more things you would have not to do in Rust, but you are hitting the most fundamental problem here).

Most other modern, popular languages encourage “soup of pointers” design where you don't need to decide who owns what upfront, but can decide that later. This doesn't work, of course, thus there are bazillion books and lots of design patterns which describe how you can introduce some ownership notion into these languages, but that happens later. You can always start with “soup of pointers” design and then [try to] refine it later.

Rust, in stark divergence from the norm, makes question "who owns what" central part of the whole design. Cue to the problem you are having. No, it's not problem of “using self in closure”. It problem of unclear ownership: in your design handler has unrestricted access to self from some other thread and init, too, have unrestricted access!

This design would never be accepted by Rust compiler without additional questions. Rust does allow shared access but wants you to explicitly describe rules of ownership. Only if it's satisfied with the explanation it would accept the program.

That's why designs popular in other languages often don't work with Rust: they actually include lots of potentially dangerous corner cases (but people just ignore them).

Tony Hoare write: There are two ways to write code: write code so simple there are obviously no bugs in it, or write code so complex that there are no obvious bugs in it.

And Rust, like Haskell, pushes you, hard into making sure your program have Hoare Property: code so complex that there are no obvious bugs in it is not an option.

That's why people find it so hard to learn it: all their previous experience pushes them to design things in a way that Rust compiler would just never accept.

P.S. It's not impossible to convert program from any other language into Rust, just, mostly pointless. You would spend lots of time tricking compiler into allowing code it wants to reject and the end result would be even bigger mess than code which you used as an inspiration. What's the point in all that, in such a case?

7 Likes

Thanks :+1: :+1: :+1:. This solution works for me.
I am a newbie in rust, and it is hard to start really.
Thanks again.

That's a good idea, thanks for your answer.
Thanks :+1: :+1: :+1:

Thanks :+1: :+1: :+1:
I am a newbie in rust. I will try and learn from your suggestion.

I am a newbie in rust, and I migrate java/js/python's experience to rust, this process often hits a wall however.
Your answer goes straight to the essence of my problem.
Thanks :+1: :+1: :+1:.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.