I got a problem understanding the scope with rust. I try to implement a simple code to play a sound. I want to fill a buffer then play it, but I can’t get it compiling.
I got this method:
fn main() { let channels: usize = 2; let device = initialize_device(); let config = initialize_configuration(&device, channels as u16); let mut stream_buffer = [0.0 as f32; 64]; { let mut oscillator = Oscillator::new(440.0 as f32, 0.0 as f32, 44100.0 as f32, &mut stream_buffer); let processor = move |_data: &mut [f32], _: &cpal::OutputCallbackInfo| { //FIXME: Send data from oscillator to _data oscillator.process(); }; let stream = build_outputstream(config, device, Box::new(processor)); let _ = stream.play(); std::thread::sleep(std::time::Duration::from_millis(2000)); } }
The variable “stream_buffer” is just before a scope
let mut stream_buffer = [0.0 as f32; 64]; { ... }
where the rest of the code is executed.
From what I understand from documentation of rust is that by default, variables are on the stack and we can box them to have them on the heap.
So, “stream_buffer” is on the stack and should stay on the stack until the end of the program. Even if I don’t manually create the scope below it.
According to the doc also, the variables on the heap are destroyed automatically when they go out of scope.
So, I understand that the lifecycle of my boxed “processor” is shorter that the “stream_buffer” and it’s destroyed at the end of its scope.
let mut stream_buffer = [0.0 as f32; 64]; { let mut oscillator = Oscillator::new(440.0 as f32, 0.0 as f32, 44100.0 as f32, &mut stream_buffer); let processor = move |_data: &mut [f32], _: &cpal::OutputCallbackInfo| { //FIXME: Send data from oscillator to _data oscillator.process(); }; let stream = build_outputstream(config, device, Box::new(processor)); ... }
But the compiler complains that “stream_buffer” does not live enough…
error[E0597]: `stream_buffer` does not live long enough
--> srcmain.rs:42:88
|
40 | let mut stream_buffer = [0.0 as f32; 64];
| ----------------- binding `stream_buffer` declared here
41 | {
42 | let mut oscillator = Oscillator::new(440.0 as f32, 0.0 as f32, 44100.0 as f32, &mut stream_buffer);
| ^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
49 | let stream = build_outputstream(config, device, Box::new(processor));
| ------------------- cast requires that `stream_buffer` is borrowed for `'static`
...
55 | }
| - `stream_buffer` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn for<'a, 'b> FnMut(&'a mut [f32], &'b OutputCallbackInfo) + Send>` actually means `Box<(dyn for<'a, 'b> FnMut(&'a mut [f32], &'b OutputCallbackInfo) + Send + 'static)>`
If I put the “stream_buffer” as static as the compiler suggest, it works. But I don’t want to.
I think I understand that because processor must be on the heap, I have no choice but make the “stream_buffer” static ? Can’t I have the “stream_buffer” on the stack and the processor on the heap ?
Thank a lot for your explanations ! 🙂
1