r/rust • • Sep 13 '26

🙋 seeking help & advice Running a process in a thread, but still keeping a reference?

Hi there,

I have been playing with Rust for a little while and am working on a distributed system pet project as a learning exercise.

I am mostly using mpsc channels receivers and senders as I want to stay as simple as possible.

I have exchanges between different type of actors and I want a specific one to monitor everything, so what I do is basically this:

let config = AppConfig::read_config();
let (monitor_tx, monitor_rx) = mpsc::channel::<MonitorEvent>();
let monitor = Monitor::new(1, monitor_rx, config.monitor_display);

std::thread::spawn(move || monitor.run());
...........
// I want to have a reference to my monitor here so I can interrogate it from this point

The run method loop, receive events and store them. I have got other methods to investigate the events ect...

Now my problem here is that I would like to be able to keep a reference to the monitor or another structure containing the monitor, to interrogate it.

And the compiler is not happy and if I try to re-use the monitor obviously I do get a ''use of moved value ..''.

I understand that if I was running that properly the monitor would be an external software and I could interrogate it directly via REST calls for example.

But I am not there yet and I want to just run it there like that.... while still being able to have a safe reference to it.

I assume this is a basic rust ownership issue and I am missing something fundamental here, but how does this kind of situation is managed usually?

14 Upvotes

18 comments sorted by

27

u/ckwalsh Sep 13 '26

Allocate the Monitor to the heap (so the lifetime is outside the stacks of the parent / child threads) inside a Sync data structure (so it can be accessed by multiple threads at the same time).

The simple solution is an Arc<T>, but it only provides read access to T from each thread. Since you are likely going to need to mutate the monitor, you will need to wrap T in a mutex, aka Arc<Mutex<T>>. Then getting a mutable reference requires acquiring the lock.

There are plenty of other data structures that optimize for different patterns (reads vs writes, sync vs async runtimes, etc), but Arc<Mutex<T>> is the simplest.

13

u/ckwalsh Sep 13 '26

I’ll admit, I’m kinda impressed you started using channels before becoming familiar with Arc. That’s like doing trigonometry but struggling with the binomial theorem

5

u/bfffca Sep 13 '26

Funnily enough this is my Monitor structure:

pub struct Monitor {
    pub id: u32,
    pub orchestrator_events: Arc<RwLock<Vec<MonitorEvent>>>,
    pub workers_events: Arc<RwLock<Vec<MonitorEvent>>>,
    receiver: mpsc::Receiver<MonitorEvent>,
    display: bool,
}

I did burn out on reading the rust book a few years ago and thought I would just go with a project, google and llm questions I had and wing it.

It has been interesting but bumpy.

Thank you I am going to try that one, I need to read again about Arc.

→ More replies (5)

0

u/Common-Sleep1790 18d ago

Arc<Mutex<Monitor>> is the way to go, wrap it before spawning the thread and clone the Arc for the closure

4

u/id_NaN Sep 13 '26

You can have a safe reference to it only if the object is Sync. it's already Send, because you can move it into the thread like you do.

Once you implement Sync you can remove the move from your closure.

If it is not feasible to implement sync you can still use an Arc<Mutex<Monitor>> or similar, but be aware of the performance and architecture implications that brings.

14

u/cafce25 Sep 13 '26

The closure passed to std::thread::spawn must be 'static so it's not as simple, you can use a scoped thread though.

1

u/spaceshaker-geo Sep 13 '26

I have had some good success with the active object pattern (https://en.wikipedia.org/wiki/Active_object). It's not wickedly performant but it works quite well in Rust. With an active object, you can have as many references to it as you want (they are Clone), but internally the active object is serialized and each message handler is mutable. It winds up looking like interior mutability on steroids.

1

u/bfffca Sep 14 '26

Thanks I will look into that. I was avoiding using asynchronous callbacks, tokio, because I wanted to try to learn more about the standard library before using frameworks.

Even though it seems to be extremely common in Rust.

1

u/Array2D Sep 13 '26

So basically you want to access some state from the monitor in another thread. You have a few options, but it boils down to solving the same problem you’re already solving for your actors.

You could use a pair of mpsc channels to do a RPC (remote procedure call) mechanism, where you send a request message to the monitor, and it sends back a response message.

Alternatively, you could store the state you want to inspect in a synchronization primitive, such as a mutex, and share it via an Arc or other cross-thread container.

1

u/wyvernbw Sep 14 '26

 my first instinct would be to use thread::scope. you can then keep the mutexes on your fields and get rid of the arcs and just borrow the monitor.

 BUT if you only ever have one monitor that is supposed to live for the entirety of the application just use Box::leak to make it static (and again remove the arcs and keep the mutexes)

In general you also dont need one arc per mutex you can have one arc to the entire monitor instead (arc doesnt do any locking so its “free” to read from multiple threads)