I'm following the sine example in the rust-portaudio examples directory (which uses the non-blocking API), and I'm trying to get run()
working with a Trait type argument instead of calculating the samples within run()
itself.
The trait I've defined is pretty simple:
pub trait Evaluatable {
fn evaluate(&mut self) -> (f32, f32);
}
I changed the run()
function signature to the following to accept my trait:
fn run<E: Evaluatable + 'static>(mut generator: E) -> Result<(), pa::Error>
and upated the callback function to call evaluate()
on the trait type instead of generating samples itself:
let callback = move |pa::OutputStreamCallbackArgs { buffer, frames, .. }| {
let mut idx = 0;
for _ in 0..frames {
let samples = generator.evaluate();
buffer[idx] = samples.0;
buffer[idx+1] = samples.1;
idx += 2;
}
pa::Continue
};
E
has to be 'static
due to this callback (see open_non_blocking_stream()
), which is the main source of my frustration...
In main
, I can create an Evaluatable
type and pass it in just fine (aside: I'm surprised this works, do objects in main
get 'static
lifetime?):
fn main() {
// Implements Evaluatable
// arguments are: sample rate, frequency, amplitude
let mut sine_generator = SineGenerator::new(44100.0, 200.0, 0.3);
run(sine_generator).unwrap()
}
But I want to be able to combine signals, so I made a struct that can combine them:
use evaluatable::Evaluatable;
pub struct Combine<'a> {
first: &'a mut (Evaluatable + 'a),
second: &'a mut (Evaluatable + 'a),
}
impl<'a> Combine<'a> {
pub fn new(first: &'a mut Evaluatable, second: &'a mut Evaluatable) -> Combine<'a> {
Combine {first, second}
}
}
impl<'a> Evaluatable for Combine<'a> {
fn evaluate(&mut self) -> (f32, f32) {
let first_output = self.first.evaluate();
let second_output = self.second.evaluate();
(first_output.0 + second_output.0, first_output.1 + second_output.1)
}
}
And tried to use it in main()
:
fn main() {
let mut sine_generator1 = SineGenerator::new(44100.0, 200.0, 0.3);
let mut sine_generator2 = SineGenerator::new(44100.0, 250.0, 0.3);
let combine = Combine::new(&mut sine_generator1, &mut sine_generator2);
run(combine).unwrap()
}
Now, Rust seems to have a problem with the lifetimes not being 'static
:
Error[E0597]: `sine_generator1` does not live long enough
--> src/main.rs:27:37
|
27 | let combine = Combine::new(&mut sine_generator1, &mut sine_generator2);
| ^^^^^^^^^^^^^^^ does not live long enough
28 | run(combine).unwrap()
29 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
- Why was Rust able to allow me to use
SineGenerator
frommain()
, but won't let me useCombine
which takes the same objects (with, I assume, the same lifetimes)? - Is there a better way to implement
Combine
that will allow me to do what I want to do here? I had to take references because Trait types don't have defined size at compile-time.