我正在编写一个简单的 Rust 程序,它将足够响亮的音频输入传输到输出设备。
我通过使用ringbuf获得输入和输出回调来共享状态,其方式深受此代码的启发。
但是,当我尝试在函数中使用我的结构之一的可变引用时,我遇到了错误 E0525:
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/main.rs:59:25
|
59 | ...et input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
60 | ... let mut output_fell_behind = false;
61 | ... massager.ingest(data.to_vec());
| -------- closure is `FnOnce` because it moves the variable `massager` out of its environment
...
84 | ...et input_stream = input.build_input_stream(&config, input_data_fn, err_f...
| ------------------ ------------- the requirement to implement `FnMut` derives from here
| |
| required by a bound introduced by this call
|
note: required by a bound in `build_input_stream`
--> C:\Users\info\.cargo\registry\src\index.crates.io-6f17d22bba15001f\cpal-0.15.3\src\traits.rs:134:12
|
125 | fn build_input_stream<T, D, E>(
| ------------------ required by a bound in this associated function
...
134 | D: FnMut(&[T], &InputCallbackInfo) + Send + 'static,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `DeviceTrait::build_input_stream`
For more information about this error, try `rustc --explain E0525`.
这是产生此错误的代码的简化最小示例(以及完整源代码,如果有人感兴趣):
注意:对长示例表示歉意,但是
build_input_stream
是必要的,因为它要求闭包实现 FnMut
特性。
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::{
traits::{Producer, Split},
HeapRb,
};
fn main() -> anyhow::Result<()> {
// Setting up audio devices, ignore this code
let host = cpal::default_host();
let input = host
.default_input_device()
.expect("No input device available");
println!(
"Using output device {}",
input.name().unwrap_or("unnamed".to_owned())
);
let input_config = input
.default_input_config()
.expect("No default input config");
let input_sample_format = input_config.sample_format();
let input_sample_rate = input_config.sample_rate().0 as f32;
let input_channels = input_config.channels() as usize;
let output = host
.default_output_device()
.expect("No output device available");
println!(
"Using output device {}",
output.name().unwrap_or("unnamed".to_owned())
);
let output_config = output
.default_output_config()
.expect("No default output config");
let output_sample_format = output_config.sample_format();
let output_sample_rate = output_config.sample_rate().0 as f32;
let output_channels = output_config.channels() as usize;
let config: cpal::StreamConfig = output_config.into();
let latency_frames = 1.0 * config.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * output_channels as usize;
let ring = HeapRb::<f32>::new(latency_samples * 2);
// MUTABLE VARIABLES FROM A 3RD PARTY LIBRARY:
let (mut producer, mut consumer) = ring.split();
for _ in 0..latency_samples {
producer.try_push(0.0).unwrap();
}
// MY MUTABLE VARIABLES:
let mut massager = DataMassager::new();
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
let mut output_fell_behind = false;
// THE CALL THAT IS CAUSING PROBLEMS
massager.ingest(data.to_vec());
match massager.next() {
Some(samples) => {
for sample in samples {
if producer.try_push(sample).is_err() {
output_fell_behind = true;
}
}
}
None => {
for i in 0..data.len() {
if producer.try_push(0.0).is_err() {
output_fell_behind = true;
}
}
}
};
if output_fell_behind {
eprintln!("output stream fell behind: try increasing latency");
}
};
let input_stream = input.build_input_stream(&config, input_data_fn, err_fn, None)?;
input_stream.play()?;
Ok(())
}
fn err_fn(err: cpal::StreamError) {
eprintln!("an error occurred on stream: {}", err);
}
// A useless iterator to illustrate this bug
pub struct DataMassager {
buffer: Vec<Vec<f32>>,
}
impl DataMassager {
pub fn new() -> Self {
let buffer: Vec<Vec<f32>> = vec![];
return DataMassager { buffer };
}
pub fn ingest(mut self, chunk: Vec<f32>) {
self.buffer.insert(0, chunk);
}
}
impl Iterator for DataMassager {
type Item = Vec<f32>;
fn next(&mut self) -> Option<Self::Item> {
return self.buffer.pop();
}
}
我想知道为什么可变函数 try_push 没有导致与我的
ingest
函数相同的错误。原来答案就在签名里:
fn try_push(&mut self, elem: Self::Item) -> Result<(), Self::Item> {
它使用
&mut
——可变引用。
我的代码无法编译的原因是我在没有任何引用的情况下使用了
mut
,表示移动而不是借用。
pub fn ingest(mut self, chunk: Chunk) {
解决方案是添加一个字符
pub fn ingest(&mut self, chunk: Chunk) {
如果没有 build_input_stream
上的
constraints,这段代码会导致更直接的
borrow of moved value
错误。据我所知,这是根本错误。