我一直在通过 Rust 和 Rust 库使用 AudioUnit coreaudio-rs .他们的例子似乎运作良好:
extern crate coreaudio;
use coreaudio::audio_unit::{AudioUnit, IOType};
use coreaudio::audio_unit::render_callback::{self, data};
use std::f32::consts::PI;
struct Iter {
value: f32,
}
impl Iterator for Iter {
type Item = [f32; 2];
fn next(&mut self) -> Option<[f32; 2]> {
self.value += 440.0 / 44_100.0;
let amp = (self.value * PI * 2.0).sin() as f32 * 0.15;
Some([amp, amp])
}
}
fn main() {
run().unwrap()
}
fn run() -> Result<(), coreaudio::Error> {
// 440hz sine wave generator.
let mut samples = Iter { value: 0.0 };
//let buf: Vec<[f32; 2]> = vec![[0.0, 0.0]];
//let mut samples = buf.iter();
// Construct an Output audio unit that delivers audio to the default output device.
let mut audio_unit = try!(AudioUnit::new(IOType::DefaultOutput));
// Q: What is this type?
let callback = move |args| {
let Args { num_frames, mut data, .. } = args;
for i in 0..num_frames {
let sample = samples.next().unwrap();
for (channel_idx, channel) in data.channels_mut().enumerate() {
channel[i] = sample[channel_idx];
}
}
Ok(())
};
type Args = render_callback::Args<data::NonInterleaved<f32>>;
try!(audio_unit.set_render_callback(callback));
try!(audio_unit.start());
std::thread::sleep(std::time::Duration::from_millis(30000));
Ok(())
}
但是,将其稍微更改为通过缓冲区加载也不起作用:
extern crate coreaudio;
use coreaudio::audio_unit::{AudioUnit, IOType};
use coreaudio::audio_unit::render_callback::{self, data};
fn main() {
run().unwrap()
}
fn run() -> Result<(), coreaudio::Error> {
let buf: Vec<[f32; 2]> = vec![[0.0, 0.0]];
let mut samples = buf.iter();
// Construct an Output audio unit that delivers audio to the default output device.
let mut audio_unit = try!(AudioUnit::new(IOType::DefaultOutput));
// Q: What is this type?
let callback = move |args| {
let Args { num_frames, mut data, .. } = args;
for i in 0..num_frames {
let sample = samples.next().unwrap();
for (channel_idx, channel) in data.channels_mut().enumerate() {
channel[i] = sample[channel_idx];
}
}
Ok(())
};
type Args = render_callback::Args<data::NonInterleaved<f32>>;
try!(audio_unit.set_render_callback(callback));
try!(audio_unit.start());
std::thread::sleep(std::time::Duration::from_millis(30000));
Ok(())
}
它正确地说,buf
只存在到 run
结束并且没有足够长的时间供音频单元使用——这是有道理的,因为“借来的值必须在静态生命周期内有效...”。
无论如何,这不会打扰我;我可以修改迭代器以从缓冲区加载和读取就好了。但是,它确实提出了一些问题:
- 为什么
Iter { value: 0.0 }
有'static
生命周期? - 如果它没有
'static
生命周期,为什么说借用的值必须在'static
生命周期内有效? - 如果它确实有
'static
生命周期,为什么?看起来它会在堆上并由callback
关闭。 - 我知道
move
关键字允许在闭包内移动,这无助于我理解为什么它与生命周期交互。为什么它不能移动缓冲区?我是否必须将缓冲区和迭代器都移动到闭包中?我该怎么做? - 综上所述,我如何在不尝试自己成为编译器的情况下计算出预期的生命周期?猜测和编译似乎并不总是解决这些问题的直接方法。
最佳答案
Why does the
Iter { value: 0.0 }
have the'static
lifetime?
它没有;只有引用有生命周期。
why does it say the borrowed value must be valid for the
'static
lifetimehow do I figure out the expected lifetime without trying to be a compiler myself
阅读文档;它告诉你限制:
fn set_render_callback<F, D>(&mut self, f: F) -> Result<(), Error>
where
F: FnMut(Args<D>) -> Result<(), ()> + 'static, // <====
D: Data
此限制意味着 F
内的任何引用必须至少与 'static
生命周期一样长。没有引用文献也是可以接受的。
所有类型和生命周期限制都在函数边界表示——这是 Rust 的硬性规则。
I understand that the
move
keyword allows moving inside the closure, which doesn't help me understand why it interacts with lifetimes.
move
关键字所做的唯一一件事就是强制将闭包中直接使用 的每个变量移动到闭包中。否则,编译器会尝试保守并根据闭包内的用法移入引用/可变引用/值。
Why can't it move the buffer?
变量 buf
永远不会在闭包内部使用。
Do I have to move both the buffer and the iterator into the closure? How would I do that?
通过在闭包内创建迭代器。现在 buf
被用在闭包内部并且将被移动:
let callback = move |args| {
let mut samples = buf.iter();
// ...
}
It doesn't seem like guessing and compiling is always a straightforward method to resolving these issues.
有时是这样,有时您必须考虑为什么您认为代码是正确的以及为什么编译器声明它不是正确的,并达成共识。
关于rust - 精明的一生理解移动关键字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44080917/