How can I use the value in std::sync::MutexGuard after locking it?

I need to play audio after crawling them from the site. I am using crate rodio to play audios. How can I get the iterator of variable audio: Vec<Decoder<std::io::BufReader<std::fs::File>>> in the following code?

let audios: Vec<Decoder<_>> = Vec::new();
let aud_mutex: Arc<Mutex<Vec<_>>> = Arc::new(Mutex::new(audios));
// I grab data from the web and write audio bufs into aud_mutex
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let audios = aud_mutex.lock().unwrap();
for source in audios {
    stream_handle.play_raw(source.convert_samples());
}

The above code produces the following error.

error[E0277]: `std::sync::MutexGuard<'_, Vec<Decoder<std::io::BufReader<std::fs::File>>>>` is not an iterator
   --> src\entries.rs:179:23
    |
179 |         for source in audios {
    |                       ^^^^^^ `std::sync::MutexGuard<'_, Vec<Decoder<std::io::BufReader<std::fs::File>>>>` is not an iterator   
    |
    = help: the trait `Iterator` is not implemented for `std::sync::MutexGuard<'_, Vec<Decoder<std::io::BufReader<std::fs::File>>>>` 
    = note: required for `std::sync::MutexGuard<'_, Vec<Decoder<std::io::BufReader<std::fs::File>>>>` to implement `IntoIterator`

How can I solve the problem?

TL;DR change for source in audios to for source in &audios or for source in audios.iter() (they are the same thing in this case because of the way that Vec is implemented).

The MutexGuard types uses a Deref implementation, which means that you can't move it - you have to use it by ref. The for _ in _ language construct uses IntoIterator (the trait), and that trait is not implemented for MutexGuard. IntoIterator isn't implemented for &MutexGuard either, but because it is behind a reference the compiler does 'automatic deref' using the Deref trait (it runs the Deref::deref method then sees if there is an IntoIterator on the target type, in this case &Vec). There is an impl of IntoIterator for &Vec, so that is used.

1 Like

However, another error occurs when I modify the code as follows. I would like to take the ownership of individual elements. How can I do that?

let audios = aud_mutex.lock().unwrap();
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
for source in audios.iter() {
    stream_handle.play_raw(source.convert_samples());
}
error[E0507]: cannot move out of `*source` which is behind a shared reference
   --> src\entries.rs:181:36
    |
181 |             stream_handle.play_raw(source.convert_samples());
    |                                    ^^^^^^^-----------------
    |                                    |      |
    |                                    |      `*source` moved due to this method call
    |                                    move occurs because `*source` has type `Decoder<std::io::BufReader<std::fs::File>>`, which does not implement the `Copy` trait
    |
note: this function takes ownership of the receiver `self`, which moves `*source`
   --> D:\Scoop\persist\rustup-msvc\.cargo\registry\src\github.com-1ecc6299db9ec823\rodio-0.16.0\src\source\mod.rs:292:27
    |
292 |     fn convert_samples<D>(self) -> SamplesConverter<Self, D>
    |                           ^^^^

For more information about this error, try `rustc --explain E0507`.

If you want to take the samples out of the lock and leave an empty Vec there, you can write:

let audios:Vec<_> = std::mem::take(aud_mutex.lock().unwrap());

(untested, might need to be &mut aud_mutex… or &mut * aud_mutex… instead)

1 Like

Thanks!