1

I am trying to realize the following function, which takes 3 optional input and get search result from mpd client from the rust-mpd crate.

fn load(
    &mut self,
    track: &Option<String>,
    artist: &Option<String>,
    album: &Option<String>
) -> Result<(), &'static str>{
    println!("mpd, load method is invoked with:");
    let mut query = Query::new();

    if let Some(s) = track {
        println!("  track: {}", &s);
        query.and(Term::Tag(Cow::Borrowed("track")), s);
    }

    if let Some(s) = artist {
        println!("  artist: {}", &s);
        query.and(Term::Tag(Cow::Borrowed("artist")), s);
    }

    if let Some(s) = album {
        println!("  album: {}", &s);
        query.and(Term::Tag(Cow::Borrowed("album")), s);
    }

    //let musics = self.client.search(&query, Window(None));
    let musics = self.client.search(&query, (1,2));
    println!("Search result: {:?}", musics);

    Ok(())
}

However, when I try to compile, it tells me:

error[E0499]: cannot borrow `query` as mutable more than once at a time
  --> src/player/mpd.rs:62:13
   |
57 |             query.and(Term::Tag(Cow::Borrowed("track")), s);
   |             ----- first mutable borrow occurs here
...
62 |             query.and(Term::Tag(Cow::Borrowed("artist")), s);
   |             ^^^^^
   |             |
   |             second mutable borrow occurs here
   |             first borrow later used here

error[E0499]: cannot borrow `query` as mutable more than once at a time
  --> src/player/mpd.rs:67:13
   |
62 |             query.and(Term::Tag(Cow::Borrowed("artist")), s);
   |             ----- first mutable borrow occurs here
...
67 |             query.and(Term::Tag(Cow::Borrowed("album")), s);
   |             ^^^^^
   |             |
   |             second mutable borrow occurs here
   |             first borrow later used here

error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
  --> src/player/mpd.rs:71:41
   |
67 |             query.and(Term::Tag(Cow::Borrowed("album")), s);
   |             ----- mutable borrow occurs here
...
71 |         let musics = self.client.search(&query, (1,2));
   |                                         ^^^^^^
   |                                         |
   |                                         immutable borrow occurs here
   |                                         mutable borrow later used here

If my way is wrong, how should I achieve the same functionality?

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
Polar Bear
  • 71
  • 1
  • 6

2 Answers2

3

On first sight, the mutable borrow should end with the if let block, which would allow the subsequent borrows. However there is a bug in rust-mpd that causes the borrow to be extended for the full lifetime of query: the Query::and method is declared as:

fn and<'b: 'a, V: 'b + Into<Cow<'b, str>>>(
    &'a mut self,         // <- Note the 'a lifetime here
    term: Term<'b>,
    value: V)
    -> &'a mut Query<'a>  // <- Note the **two** 'a lifetimes here

using the same lifetime 'a for both the references to the query and the embedded lifetime. The method should be declared like this:

fn and<'b: 'a, V: 'b + Into<Cow<'b, str>>>(
    &mut self,
    term: Term<'b>,
    value: V)
    -> &mut Query<'a>

You should report a bug to rust-mpd.

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • 1
    Feel free to point the mpd maintainer to [Cannot borrow as mutable more than once at a time in one code - but can in another very similar](https://stackoverflow.com/q/31067031/155423) – Shepmaster Nov 15 '19 at 14:14
1

If you just want to use the library in its non-ideal state, you can iteratively update a mutable reference to the Query, instead of using it directly:

use mpd::{Query, Term}; // 0.0.12
use std::borrow::Cow;

fn load(
    track: &Option<String>,
    artist: &Option<String>,
    album: &Option<String>,
) -> Result<(), &'static str> {
    let mut query = Query::new();
    let mut query = &mut query;

    if let Some(s) = track {
        query = query.and(Term::Tag(Cow::Borrowed("track")), s);
    }

    if let Some(s) = artist {
        query = query.and(Term::Tag(Cow::Borrowed("artist")), s);
    }

    if let Some(s) = album {
        query = query.and(Term::Tag(Cow::Borrowed("album")), s);
    }

    let mut client = mpd::Client::new(std::fs::File::open("/tmp/junk").unwrap()).unwrap();
    let _musics = client.search(&query, (1, 2));

    Ok(())
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366