This is an experiment I'm doing while learning Rust and following Programming Rust.
Here's a link to the code in the playground.
I have a struct (Thing
) with some inner state (xs
). A Thing
should be created with Thing::new
and then start
ed, after which the user should choose to call some other function like get_xs
.
But! In start
2 threads are spawn
ed which call other methods on the Thing
instance that could mutate its inner state (say, add elements to xs
), so they need a reference to self
(hence the Arc
). However, this causes a lifetime conflict:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:18:30
|
18 | let self1 = Arc::new(self);
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined
on the method body at 17:5...
--> src/main.rs:17:5
|
17 | / fn start(&self) -> io::Result<Vec<JoinHandle<()>>> {
18 | | let self1 = Arc::new(self);
19 | | let self2 = self1.clone();
20 | |
... |
33 | | Ok(vec![handle1, handle2])
34 | | }
| |_____^
note: ...so that expression is assignable (expected &Thing, found &Thing)
--> src/main.rs:18:30
|
18 | let self1 = Arc::new(self);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/main.rs:23:20: 25:14
self1:std::sync::Arc<&Thing>]` will meet its required lifetime bounds
--> src/main.rs:23:14
|
23 | .spawn(move || loop {
| ^^^^^
Is there a way of spawning the state-mutating threads and still give back ownership of thing
after running start
to the code that's using it?
use std::io;
use std::sync::{Arc, LockResult, RwLock, RwLockReadGuard};
use std::thread::{Builder, JoinHandle};
struct Thing {
xs: RwLock<Vec<String>>
}
impl Thing {
fn new() -> Thing {
Thing {
xs: RwLock::new(Vec::new()),
}
}
fn start(&self) -> io::Result<Vec<JoinHandle<()>>> {
let self1 = Arc::new(self);
let self2 = self1.clone();
let handle1 = Builder::new()
.name("thread1".to_owned())
.spawn(move || loop {
self1.do_within_thread1();
})?;
let handle2 = Builder::new()
.name("thread2".to_owned())
.spawn(move || loop {
self2.do_within_thread2();
})?;
Ok(vec![handle1, handle2])
}
fn get_xs(&self) -> LockResult<RwLockReadGuard<Vec<String>>> {
return self.xs.read();
}
fn do_within_thread1(&self) {
// read and potentially mutate self.xs
}
fn do_within_thread2(&self) {
// read and potentially mutate self.xs
}
}
fn main() {
let thing = Thing::new();
let handles = match thing.start() {
Ok(hs) => hs,
_ => panic!("Error"),
};
thing.get_xs();
for handle in handles {
handle.join();
}
}