Why Closure Outlives Variables Owned by main()?


#1

I just started Rust programming this week and I am trying to build a project to learn it. I encountered this problem today and I couldn’t understand why it occurred. I did search around the forum and the internet but all other questions do not seem to be applicable to mine.

The original code is very long so I abstracted it into a simplified version:

use std::{thread, time};
use std::f32;
use std::io::stdin;

struct Car {
    x: f32,
    y: f32,
}

impl Car {
    fn cur_pos(&self) -> (f32, f32) {
        (12.331, 14.112) // suppose some device returns coordinates
    }

    fn take_order(&self, order: &str) {
        println!("You want me to drive: {}", order);
    }

    fn new() -> Car {
        Car {
            x: 0.0,
            y: 0.0,
        }
    }
}


fn main() {
    let car = Car::new();
    thread::spawn(|| {
        loop {
            let (x, y) = car.cur_pos();
            println!("Current position is {}, {}", x, y);
            thread::sleep(time::Duration::from_millis(2000));
        }
    });
    let mut s = String::new();
    loop {
        stdin().read_line(&mut s).expect("Did not enter a correct string");
        car.take_order(&s);
    }
}

When compiling, I got:

error[E0373]: closure may outlive the current function, but it borrows `car`, which is owned by the current function
  --> src/simple.rs:29:19
   |
29 |     thread::spawn(|| {
   |                   ^^ may outlive borrowed value `car`
30 |         loop {
31 |             let (x, y) = car.cur_pos();
   |                          --- `car` is borrowed here
help: to force the closure to take ownership of `car` (and any other referenced variables), use the `move` keyword
   |
29 |     thread::spawn(move || {
   |                   ^^^^^^^

My understanding of this problem is, rustc thinks that the variable car will die after main() quits and the closure may still be alive and therefore the closure will reference garbage. However, this doesn’t make sense since as the thread dies when main() quits. So I think both the closure and main() should have the same life time, which is static.

Failed Approaches

I have tried to solve it by letting the closure take the ownership but the loop after it needs it so I can’t really do it.

I have also tried to warp car into Arc so we have a multi-ownership thread safe car. However, the same error persists.

Thanks.


#2

main is a function just like any other: it doesn’t get any special lifetime treatment. Other functions are free to call main at points in the program where the spawned thread would outlive the call to main.

As for a solution, I recommend using Arc and Arc::clone to do multiple ownership:

fn main() {
    let car = Arc::new(Car::new());
    let thread_car = car.clone();
    thread::spawn(|| {
        loop {
            let (x, y) = thread_car.cur_pos();
            println!("Current position is {}, {}", x, y);
            thread::sleep(time::Duration::from_millis(2000));
        }
    });
    let mut s = String::new();
    loop {
        stdin().read_line(&mut s).expect("Did not enter a correct string");
        car.take_order(&s);
    }
}

You might also want to wrap in a Mutex or RwLock if you need mutable access.


#3

Thank you for the help. Yes, with Arc<Car> it works now! I actually tried Arc but failed. I didn’t do clone() and I just (wrongfully) assumed once I warp it with Arc it would do ownership management automatically – but there’s no magic :slight_smile: