Trouble Implementing Trait for Hound Samples in Rust

Hi!

I have a hard time understanding how to write code with traits.

In the following example, I created a Trait Sample, that for each numeric type implements a function to_bytes.

My goal here is to read a file from hound create, and for each hound::Sample send it through the channel and then "process" it using the function to_bytes.

So in each step "don't care about the type of the value", only that implements the Sample Trait.

Rust instead is showing me that it cares about it.

use std::sync::Arc;

pub trait Sample
{
    fn to_bytes(&self) -> Arc<[u8]>;
}


// implement the macro for all the numeric types
macro_rules! implement_sample {
    ($($t:ty),*) => {
        $(
            impl Sample for $t {
                fn to_bytes(&self) -> Arc<[u8]> {
                    self.to_le_bytes().into()
                }
            }
        )*
    };
}

implement_sample!(u8, i8,  u16, i16, u32, i32, f32, u64, i64, f64, u128, i128, usize, isize);

async fn test_something<S: Sample>(mut rx: tokio::sync::mpsc::Receiver<S>)
{
    let mut count = 0;
    while let Some(sample) = rx.recv().await {
        let s = sample.to_bytes();
        println!("{:?}", s);
        count += 1;
    }

    println!("Received {} samples", count);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn using_hound_sample() {
        let (tx, rx) = tokio::sync::mpsc::channel(1);

        tokio::task::spawn_blocking(move || {
            let mut file = hound::WavReader::open("tests/sample.wav")
                .expect("Error opening file");

            file.samples()
                .map(|s| s.unwrap())
                .for_each(|s|  tx.blocking_send(s).unwrap())
        });

        test_something(rx).await;
    }
}


How can I instruct Rust to send the s: hound::Sample

            file.samples()
                .map(|s| s.unwrap())
                .for_each(|s|  tx.blocking_send(s).unwrap())
        });

through the tx Sender given that s will be a type (i16, i32, or f32) that implements my Sample trait?

What am I missing here?

Thanks a lot!

P.S. A link to the documentation explaining this kind of situation would be highly appreciated!

You did not include particular errors rustc gives you, but my assumption is it cannot infer the type of the sample.
Some code is allowed to not care what particular type the sample has, but at the end of the day that type should be chosen, to give the program meaning.
From the example on hound readme it can be reading samples:

file.samples:<i16>()  // or any other type

Selecting the type for the channel should also work.

Thanks for the reply.

Yes, your assumption is correct.
The problem is that I don't know the sample format being used until runtime.

Here's an updated example:

use std::sync::Arc;
use tokio::sync::mpsc::Receiver;

pub trait Sample
{
    fn to_bytes(&self) -> Arc<[u8]>;
}

pub struct Source<T>
    where T: Sample
{
    pub stream: Receiver<T>,
}

impl<T> Source<T>
    where T: Sample
{
    pub fn new(stream: Receiver<T>) -> Self {
        Source { stream }
    }

    pub fn with_sender() -> (Self, tokio::sync::mpsc::Sender<T>) {
        let (tx, rx) = tokio::sync::mpsc::channel(1);
        (Source { stream: rx }, tx)
    }
}

// implement the macro for all the numeric types
macro_rules! implement_sample {
    ($($t:ty),*) => {
        $(
            impl Sample for $t {
                fn to_bytes(&self) -> Arc<[u8]> {
                    self.to_le_bytes().into()
                }
            }
        )*
    };
}

implement_sample!(u8, i8,  u16, i16, u32, i32, f32, u64, i64, f64, u128, i128);

async fn test_something(mut rx: tokio::sync::mpsc::Receiver<impl Sample>)
{
    let mut count = 0;
    while let Some(sample) = rx.recv().await {
        let s = sample.to_bytes();
        println!("{:?}", s);
        count += 1;
    }

    println!("Received {} samples", count);
}

#[cfg(test)]
mod tests {
    use super::*;


    #[tokio::test]
    async fn using_hound_sample() {
        let (tx, rx) = tokio::sync::mpsc::channel(1);
        
        tokio::task::spawn_blocking(move || {
            let mut file = hound::WavReader::open("tests/sample.wav")
                .expect("Error opening file");
            
            match file.spec().sample_format {
                hound::SampleFormat::Int => {
                    file.samples()
                        .map(|s| s.unwrap())
                        .for_each(|s: i32| {
                            tx.blocking_send(s).unwrap();
                        });
                },
                hound::SampleFormat::Float => {
                    file.samples()
                        .map(|s| s.unwrap())
                        .for_each(|s: f32| {
                            tx.blocking_send(s).unwrap();
                        });
                }
            }
        });

        test_something(rx).await;
    }

}

the error that Rust has given me is:

error[E0308]: mismatched types
   --> src/lib.rs:91:46
    |
91  | ...                   tx.blocking_send(s).unwrap();
    |                          ------------- ^ expected `i32`, found `f32`
    |                          |
    |                          arguments to this method are incorrect
    |
help: the return type of this call is `f32` due to the type of the argument passed
   --> src/lib.rs:91:29
    |
91  | ...                   tx.blocking_send(s).unwrap();
    |                       ^^^^^^^^^^^^^^^^^-^
    |                                        |
    |                                        this argument influences the return type of `blocking_send`
note: method defined here
   --> /Users/jbernavaprah/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.37.0/src/sync/mpsc/bounded.rs:937:12
    |
937 |     pub fn blocking_send(&self, value: T) -> Result<(), SendError<T>> {
    |            ^^^^^^^^^^^^^


Do you have some suggestions on how to proceed here?

Thanks!

You're using a single channel to send i32 and f32. A channel has a fixed type.

Take a closer look at the errors and see if they make sense, now that you know the cause. This will help to diagnose problems like this in the future. The key thing is that the type of the channel is inferred based on its first usage. So then the types don't match on the second usage.

I see you're using a generic Sample here:

         async fn test_something(
            mut rx: tokio::sync::mpsc::Receiver<impl Sample>,
        ) {

This does not allow passing an rx that contains multiple types, each of which implements Sample. That's not how generics work. A single type is chosen when the channel is created.

You can use a Box<&dyn Sample> as the channel type, but this means allocating every sample. I would use an enum instead, with one variant per type, but that will probably mean other changes to your design.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.