Passing a borrowed variable to a thread from an endless loop


#1

Please consider the following code:

use std::thread;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;

fn handle(_rx:Receiver<(i32, &str)>) {
    // ...
}

fn main() {
    let s = String::from("Some text");
    
    let (tx, rx) = mpsc::channel();
    thread::spawn(move|| handle(rx));

    loop {
        // Endless loop
        tx.send((5, s.as_str())).unwrap();
    }
}

the code is not compiled with the following error:

error[E0597]: `s` does not live long enough
  --> a.rs:17:21
   |
17 |         tx.send((5, s.as_str())).unwrap();
   |                     ^ does not live long enough
18 |     }
19 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

However, due to the endless loop, the s will be lived forever.

rustc 1.23.0 (766bd11c8 2018-01-01)


#2

If a panic or exit happened in the loop, then the variable would not love forever. It’s a bit much to expect the compiler to prove that none of this could happen.


TWiR quote of the week
#3

a panic in the loop causes a termination in the program


#4

Ah yes, because it is in the main thread. I don’t think the borrow checker distinguishes which thread code is in when determining lifetimes. And to be honest, I wouldn’t want code to only compile when placed in the main function!


#5

Can’t unwinding panicks be caught in the main thread?


#6

I suggest explicitly leaking s.


#7

This shows a useful limitation of Rust, when it comes to sharing references among threads. I’d suggest to rewrite the code. Some reasons:

  1. Lifetime
    As long as loops (and scopes) are not guaranteed to live forever (which they aren’t), s does not live forever. The compiler catches this right.

  2. Thread synchronization
    Rust encourages to not “communicate by sharing memory” (which you do), instead one should go the other way: “Share memory by communicating”.

  3. Scope relation and leaking
    I think one of the advantages of Rust is that variable lifetime and dropping are so closely bound to a scope. A scope should be a small unit of overseeable code. A programmer shall always be sure that variables are dropped when their scope is left. Leaking the variable - to make it outlive its scope - is not favorable. Who takes care of freeing its space then?

s is immutable anyway, so you could declare it as const in static context. If this scope is too big, another way is to send the literal “Some text” to the thread, to push the value to handle()'s stack directly.

The tradeoff for having memory safety is to make threads not share references, but memory instead.