The Goal
I have a file tree and I have independent functions that would like to process that tree. I want something to orchestrate that processing, so that each file is opened once, and then all processing functions are given a chance to run on that file's content. I want this to happen in parallel with rayon.
What I have
This is working, but not quite as I want:
use std::fs;
use std::path::PathBuf;
use rayon::prelude::*;
use crossbeam::channel::{self, Receiver};
struct WorkTreeProcessor {
file_processors: Vec<Box<Fn(&str) + Send + Sync>>,
}
impl WorkTreeProcessor {
fn add_file_processor<C, F, R>(&mut self, context: C, function: F) -> Receiver<R>
where
C: Clone + Send + Sync + 'static,
F: Fn(&C, &str) -> R + Send + Sync + 'static,
R: Send + 'static,
{
let (sender, receiver) = channel::unbounded();
self.file_processors
.push(Box::new(move |file_content| {
let result = function(&context, file_content);
let _ = sender.send(result);
}));
receiver
}
fn run(&mut self, work_tree_paths: Vec<PathBuf>) {
work_tree_paths.into_par_iter().for_each(|each_path| {
if let Ok(file_content) = fs::read_to_string(each_path) {
for each in self.file_processors.iter() {
each(&file_content);
}
}
});
}
}
What I want
Right now the context passed into each function is a reference. I want it to be a mutable reference. I can't figure out how to make that work.
My general plan is:
- Those contexts are clonable.
- If I could clone a context into each rayon thread, then I could pass it in as mutable.
- Rayons
for_each_with
method provides a mechanism to clone values into each rayon thread and pass them back to the iterator function.
If I could somehow make the file_processors
Clonable then I think that would work. I would move the context into each function. And pass the functions into for_each_with
so they and their context would get cloned as needed. But I can't make the functions clone because they are stored as trait objects.
I've also tried wrapping up each function with it's context into a separate clonable object:
#[derive(Clone)]
struct FunctionWithContext<C: Clone> {
function: Arc<Fn(&mut C)>,
context: C,
}
that could then be passed into for_each_with
. But then when I try to store these as the new file_processors
I need to fill in the template, and I get locked into only supporting one type of context.
Anyway I hope that explains my goal. How do I get this to work?
Thanks,
Jesse