Why a variable in a parent scope is “out of scope” of a child one

  Kiến thức lập trình

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

LEAVE A COMMENT