Reading a reference from within and outside a closure

Hopefully the answer would help other baby Rustaceans as well:

use std::thread;

fn main() {
   let foo = String::new(); // Anything on the heap.

   thread::spawn(move || println!("{}", &foo));
   //             ↑
   // If we move, the last line obviously will error.
   // If we not move — "the closure might outlive".
   // Is there a more elegant solution than a clone?
   //             ↓
   println!("{}", &foo);
}

Being on the heap makes little difference here, but what you do depends on what you want to do with foo.

If you only intend to read it, you can chuck it into an Arc.

If you want to write to it, you need to place it into an Arc<Mutex> or something equivalent to a Mutex to make sure that no two threads of which one is a writer access the value. (Examples include RwLock, or atomic integers).

The heap matters — as it works fine with &str. The required access is read only.

It doesn't matter. Probably you used a promoted &'static str. Your foo is on the stack too (even if it happens to contain a pointer to the heap).

Anyway, if you want threads that can reference locals, that's scoped threads. Note the different joining and panicking behaviors from spawned threads.

Playground.

1 Like

Thank you very much. I just used "foo" instead of a String to test, and being new assumed the stack/heap difference :slight_smile:

I just want to use the most "Rust" approach here — and wondering what an experienced Rustie would choose between 1) .clone() before move, 2) Arc, 3) a scoped thread. They all work — just being new I'm not sure which is more idiomatic. Heard people frown upon clones as it sometimes signals a noob code, but maybe Arc or scope is an overkill here.

I feel like a caveman fiddling with matches, pardon my curiosity. Hopefully this would help other fellow cavemen.

Edit: foo is read-only, and is not to change ever (a CLI --flag).

S/he would ask what you need the value for. For example, if you need the same reference, then you can't choose the Clone. If you don't want the (double) indirection, then you can't choose Arc. But, as it is most likely in your case, it probably doesn't matter and either is fine.

Two common solutions are:

  1. Use Arc, and make the thread have its own copy, and the outer scope its own, or

  2. Use scoped threads, so they can share a regular scope-limited reference.

The fact that something is on the heap is not sufficient. The borrow checker will fight you to ensure nothing can possibly destroy the object too soon, regardless of whether it's destroyed form the heap or somewhere else.

Thank you — and everyone for your advice. The competence and helpfulness of the Rust community is pretty surprising.

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.