У меня возникла проблема с пониманием прицела из-за ржавчины. Я пытаюсь реализовать простой код для воспроизведения звука. Я хочу заполнить буфер, а затем воспроизвести его, но не могу его скомпилировать.
Я получил этот метод:
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 находится в той же области? Итак, поток_буфер все еще жив, когда поток уничтожен?
Привет, Эгьял, извини, я действительно не понимаю, как это работает. Даже если проверка заимствований не видит внутреннюю функцию, вызов функции находится в области видимости. Итак, это также ограничено этой областью, нет? Я передаю упакованную переменную в функцию, чтобы она «создала» другую область действия для функции. Но когда функция завершается, область действия функции заканчивается, и упакованная переменная должна быть уничтожена, не так ли?
Вот минимальный пример вашей проблемы:
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>) {}
Здравствуйте, отлично, спасибо. :) Я понял ваше решение исправить мой код, но я до сих пор не понимаю, почему поле имеет статическое время жизни по умолчанию:/В документации указано (если я хорошо понял), что упакованная переменная создается в куче и что ее время жизни ограничен своей областью действия. Итак, почему я должен указывать время жизни? Еще раз спасибо.
Проблема не в Box
, а в объекте типажа внутри него, и у Box
нет связанного с ним времени жизни, из которого можно было бы получить время жизни объекта типажа.
Подсказка: процессор в штучной упаковке перемещается в
build_outputstream
, а это означает, что ваша область больше не владеет им и не несет ответственности за его удаление.