Using Trait types with rust-portaudio in non-blocking mode

Viewed 137

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...

  1. Why was Rust able to allow me to use SineGenerator from main(), but won't let me use Combine which takes the same objects (with, I assume, the same lifetimes)?
  2. 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.
1 Answers
Related