1

I'm writing a dummy program that uses a struct holding a string reference. I believe I specified the lifetimes correctly (at least, the compiler does not complain on the struct definition and method implementation).

use std::thread;

struct Wrapper<'a> {
    s: &'a String
}

impl<'a> Wrapper<'a> {
    fn new(source_str: &'a String) -> Self {
        Self {
            s: source_str
        }
    }
}

fn main() {
    let orig_str: String = "WORD".to_string();
    {
        let w = Wrapper::new(&orig_str);
        let handle = thread::spawn(move || {
            println!("{}", w.s);
        });
        handle.join().unwrap();
    }
}

Anyhow I'm getting this error:

error[E0597]: `orig_str` does not live long enough
  --> src/main.rs:18:30
   |
16 |     let orig_str: String = "WORD".to_string();
   |         -------- binding `orig_str` declared here
17 |     {
18 |         let w = Wrapper::new(&orig_str);
   |                 -------------^^^^^^^^^-
   |                 |            |
   |                 |            borrowed value does not live long enough
   |                 argument requires that `orig_str` is borrowed for `'static`
...
24 | }
   | - `orig_str` dropped here while still borrowed

I've read multiple times the borrowing theory and finally ran out of ideas.

To me it's obvious that even the orig_str is dropped in the end, the thread is joined earlier and nothing wrong is happening. Moreover, the orig_str lives longer than w. (If I remove the thread then everything compiles well).

So how else I can tell Rust compiler that my thread poses no harm?

1 Answers1

1

In order to make Rust understand that the threads will be joined at a certain point (then won't have any chance to be running and still using their references), the scopes have been introduced.

Here is your example slightly modified in order to use such a scope.

struct Wrapper<'a> {
    s: &'a String,
}

impl<'a> Wrapper<'a> {
    fn new(source_str: &'a String) -> Self {
        Self { s: source_str }
    }
}

fn main() {
    let orig_str: String = "WORD".to_string();
    let w = Wrapper::new(&orig_str);
    std::thread::scope(|s| {
        s.spawn(|| {
            println!("in thread: {:?}", w.s);
        });
    });
    println!("after thread termination, {:?} is still here", w.s);
}
/*
in thread: "WORD"
after thread termination, "WORD" is still here
*/
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • Nice, I'll check that out. And what about if I'm using `tokio::spawn` instead of `thread::spawn`? any solution I'm curious? – los vatos locos Aug 06 '23 at 12:16
  • 1
    @IgorBezzubchenko Apparently it exists a [`tokio-scoped`](https://crates.io/crates/tokio-scoped) crate dedicated to the same purpose within tokio. – prog-fh Aug 06 '23 at 17:01