Почему переменная в родительской области находится «вне области видимости» дочерней

У меня возникла проблема с пониманием прицела из-за ржавчины. Я пытаюсь реализовать простой код для воспроизведения звука. Я хочу заполнить буфер, а затем воспроизвести его, но не могу его скомпилировать.

Я получил этот метод:

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));
    }
}

Переменная «stream_buffer» находится непосредственно перед областью действия.

    let mut stream_buffer = [0.0 as f32; 64];
    {
        ...
    }

где выполняется остальная часть кода.

Насколько я понимаю из документации Rust, по умолчанию переменные находятся в стеке, и мы можем упаковать их, чтобы они были в куче. Итак, stream_buffer находится в стеке и должен оставаться в стеке до конца программы. Даже если я не создаю область под ним вручную.

Согласно документу, переменные в куче автоматически уничтожаются, когда они выходят за пределы области видимости. Итак, я понимаю, что жизненный цикл моего коробочного processor короче, чем stream_buffer, и он уничтожается в конце своего срока действия.

 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));
        ...
    }

Но компилятор жалуется, что "stream_buffer" не живет...

error[E0597]: `stream_buffer` does not live long enough
  --> src\main.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)>`

Если я поставлю stream_buffer статично, как предлагает компилятор, это сработает. Но я не хочу. Кажется, я понимаю, что, поскольку процессор должен находиться в куче, у меня нет другого выбора, кроме как сделать stream_buffer статическим? Разве я не могу иметь stream_buffer в стеке, а процессор в куче?

Подсказка: процессор в штучной упаковке перемещается в build_outputstream, а это означает, что ваша область больше не владеет им и не несет ответственности за его удаление.

PitaJ 24.04.2024 17:40

Спасибо. Но build_outputstream находится в той же области? Итак, поток_буфер все еще жив, когда поток уничтожен?

Régis Ramillien 24.04.2024 17:54

Привет, Эгьял, извини, я действительно не понимаю, как это работает. Даже если проверка заимствований не видит внутреннюю функцию, вызов функции находится в области видимости. Итак, это также ограничено этой областью, нет? Я передаю упакованную переменную в функцию, чтобы она «создала» другую область действия для функции. Но когда функция завершается, область действия функции заканчивается, и упакованная переменная должна быть уничтожена, не так ли?

Régis Ramillien 24.04.2024 18:07
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
3
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот минимальный пример вашей проблемы:

fn main() {
    let s = String::from("hello world");
    let clo = || {println!("{s}");};
    takes_boxed_fn(Box::new(clo));
    
}

fn takes_boxed_fn(_: Box<dyn Fn()>) {}

и ошибка, которую он выдает:

error[E0597]: `s` does not live long enough
 --> src/main.rs:3:30
  |
3 |     let clo = || {println!("{s}");};
  |               --             ^ borrowed value does not live long enough
  |               |
  |               value captured here
4 |     takes_boxed_fn(Box::new(clo));
  |                         ------------- cast requires that `s` is borrowed for `'static`
5 |     
6 | }
  | - `s` dropped here while still borrowed
  |
  = note: due to object lifetime defaults, `Box<dyn Fn()>` actually means `Box<(dyn Fn() + 'static)>`

есть отличная заметка, объясняющая проблему:

= note: due to object lifetime defaults, `Box<dyn Fn()>` actually means `Box<(dyn Fn() + 'static)>`

это означает, что определение takes_boxed_fn эквивалентно этому:

fn takes_boxed_fn(_: Box<dyn Fn() + 'static>) {}

что требует, чтобы ваше закрытие было статичным! К счастью, расслабиться относительно легко, просто проведите явную жизнь:

fn takes_boxed_fn<'a>(_: Box<dyn Fn() + 'a>) {}

Детская площадка

Здравствуйте, отлично, спасибо. :) Я понял ваше решение исправить мой код, но я до сих пор не понимаю, почему поле имеет статическое время жизни по умолчанию:/В документации указано (если я хорошо понял), что упакованная переменная создается в куче и что ее время жизни ограничен своей областью действия. Итак, почему я должен указывать время жизни? Еще раз спасибо.

Régis Ramillien 24.04.2024 23:07

Проблема не в Box, а в объекте типажа внутри него, и у Box нет связанного с ним времени жизни, из которого можно было бы получить время жизни объекта типажа.

cafce25 24.04.2024 23:41

Другие вопросы по теме