Dear Rustaceans,
as a Rust beginner, I am looking for a way to refactor a long closure into a function or method. Coming from Go, I am unsure on how to proceed in Rust and kindly ask you for hints and ideas. Here’s the story:
I am using gstreamer-rs to set up a sound recording pipeline. To get samples, I can create a gstreamer_app::AppSink and set callbacks for it:
fn create_pipeline() -> Result<gst::Pipeline, Error> {
// [...]
let sink = gst::ElementFactory::make("appsink", None).ok_or(MissingElement("appsink"))?;
let appsink = sink
.dynamic_cast::<gst_app::AppSink>()
.expect("Sink element is expected to be an appsink!");
appsink.set_callbacks(
gst_app::AppSinkCallbacks::new()
.new_sample(|appsink|) {
// ... LONG CLOSURE. Here I collect samples and do stuff with them.
}).build(),
);
// [...]
}
Besides other things I want to collect samples of multiple closure calls into a larger buffer. Since gstreamer uses multiple threads , I went with big_buffer: std::sync::Mutex<VecDeque<i16>>
(many thanks to @slomo for pointing me in the right direction in the gstreamer IRC channel). So now the closure captures and moves big_buffer
.
fn create_pipeline() -> Result<gst::Pipeline, Error> {
// [...]
let big_buffer = Mutex::new(VecDeque::<i16>::new());
appsink.set_callbacks(
gst_app::AppSinkCallbacks::new()
.new_sample(move |appsink|) {
// ... In the long closure, the samples eventually end up in new_samples: [i16]
let mut big_buffer = big_buffer.lock().unwrap();
big_buffer.extend(new_samples.iter());
// more stuff happening with the samples
}
// [...]
}
It it weren’t for the capturing and move of big_buffer
, refactoring the callback into a dedicated fn
would be easy for me. In Go I would put big_buffer
in a struct
and then use a method of that struct as parameter to new_sample
.
But what would be the idiomatic way for Rust?
I have the following code working, but I lack the Rust experience to judge it and would appreciate a review.
/// A helper struct for providing the variables otherwise captured by a closure.
struct BB {
big_buffer: VecDeque<i16>,
}
impl BB {
fn new() -> BB {
let b = BB {
big_buffer: VecDeque::<i16>::new(),
};
b
}
fn long_closure(&mut self, appsink: &gst_app::AppSink) -> gst::FlowReturn {
// ALL THE LONG STUFF
}
}
// [...]
fn create_pipeline() -> Result<gst::Pipeline, Error> {
// [...]
let b = Mutex::new(BB::new()); // instead of the Mutex<VecDeque<i16>> from before
appsink.set_callbacks(
gst_app::AppSinkCallbacks::new()
.new_sample(move |appsink| {
let mut b = b.lock().unwrap();
b.long_closure(appsink)
}).build(),
);
// [...]
}