E0502 happens after immutable borrow has already ended - Why?

Dear Rustaceans,

here again a problem w.r.t. E0502. Alas, I cannot find an explanation myself by reading
the earlier postings.

Just for learning Rust, I'm trying this:

fn my_splitter( _aStr: &str ) -> Vec< &str > {
          /* This code is working well, returning a new _nVec: Vec< &str > */
}

fn main() {
   let mut _args: Vec<String> = std::env::args().collect();
   _args[ 0 ] = "try other value".to_string();  /* Simple println!() dropped, before and after */
	  for s in my_splitter( &_args[ 0 ] ).iter() {
	      println!( "//TT: s='{}'", s ); //ok, if code is running without the next line
	      _args.push( s.to_string() );   //error[E0502] - details see below
	  }
   println!( "//T: _args='{:?}'", _args );
}

Alas, getting error[E0502], which seems strange since it looks as if the immutable borrow
has already ended before the mutable borrow is tried:

error[E0502]: cannot borrow `_args` as mutable because it is also borrowed as immutable
  --> src/main.rs:25:9
   |
23 |     for s in my_splitter( &_args[ 0 ] ).iter() {
   |                            -----             - immutable borrow ends here
   |                            |
   |                            immutable borrow occurs here
24 |         println!( "//TT: s='{}'", s );
25 |         _args.push( s.to_string() );
   |         ^^^^^ mutable borrow occurs here

error: aborting due to previous error

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

And rustc --explain E0502 didn't help me out.

Can anybody help me understanding this? - And/Or proposing a working solution?

Thanks.
guevol

PS: My background includes C/C++, Java, JavaScript; but I'm a Rust newbie - obviously :wink:

First off, please put your code in a code block by bracketing with "```". It makes it much easier to read.

I'm also new to rust so I might be wrong, but it looks like _args is borrowed as immutable because of your call to my_splitter( &_args[0]).iter().

my_splitter gets an immutable reference to _args[0] that lives until the iterator goes out of scope. It would be helpful to see the source of your my_splitter() function. That way I could test your code, but maybe it does something like this?

fn my_splitter( _aStr: &str ) -> Vec< &str > {
    _aStr.split_whitespace().collect()
}

I got your code running by passing a cloned value to my_splitter instead of a reference to the value owned by _args. playground here

fn main() {
    //let mut _args: Vec = std::env::args().collect();
    let mut _args: Vec<String> = Vec::new();
    _args.push( "try other value".to_string() );
    for s in my_splitter(&_args[0].clone()).iter() {
        println!("//TT: s='{}'", s);
        _args.push(s.to_string());
    }
    println!("//T: _args='{:?}'", _args);
}

Side note, the underscore variable syntax is used to indicate that a variable will never be used. You're obviously going to use this variable, so just args would be a better name.

Repetition of my first reply, which is not displayed for unknow reason...

Thanks for your quick reply and sorry for the bad format - I had tried HTML-tt-blocks.
With the essential code of my_splitter() and all formatted within triple-backticks:

fn my_splitter( aStr: &str ) -> Vec< &str > { /* Without its println!() lines */
   let nVec: Vec< &str > = aStr.split( ' ' ).collect();
   nVec
}

fn main() { /* Without some of its println!() */
    let mut my_args: Vec<String> = std::env::args().collect();
    my_args[ 0 ] = "try other value".to_string();
    for s in my_splitter( &my_args[ 0 ] ).iter() {
        println!( "//TT: s='{}'", s ); //ok, if code is running without the next line
        my_args.push( s.to_string() ); //error[E0502] - details see below
    }
    println!( "//T: my_args='{:?}'", my_args );
}

And still the same error due to the merely non-essential changes:

error[E0502]: cannot borrow `my_args` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:9
   |
16 |     for s in my_splitter( &my_args[ 0 ] ).iter() {
   |                            -------             - immutable borrow ends here
   |                            |
   |                            immutable borrow occurs here
17 |         println!( "//TT: s='{}'", s );
18 |         my_args.push( s.to_string() );
   |         ^^^^^^^ mutable borrow occurs here

Please, be aware that .iter() concerns the returned Vec object, which, in order to block
the original my_args, must contain references to original entries of my_args. BUT why
then is the compiler claiming "immutable borrow ends here" at that very spot?

OK, I think, the crux is what I just said about the references to original entries.
Do you agree?

Alas, my quick try fails, too:

old: let nVec: Vec< &str > = aStr.split( ' ' ).collect();

was replaced by let nVec: Vec< &str > = aStr.split( ' ' ).clone().collect();

still producing the very same error...

Is there a reason that this .clone() is not sufficient to solve your problem?

I'm also unable to recreate your error. I get the following error from your original code, which makes the clone solution seem pretty straight forward.

error[E0502]: cannot borrow `_args` as mutable because it is also borrowed as immutable
 --> src/main.rs:9:9
  |
7 |     for s in my_splitter(&_args[0]).iter() {
  |              -----------------------------
  |              |            |
  |              |            immutable borrow occurs here
  |              immutable borrow later used here
8 |         println!("//TT: s='{}'", s);
9 |         _args.push(s.to_string());
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

a) OK, your proposal of calling my_splitter() with a clone is working well;
while within my_splitter() I can stick to the "old" variant
let nVec: Vec< &str > = aStr.split( ' ' ).collect();
Alas, that puts some burden on the user of my_splitter().

b) Hence, why is my forgoing try with
let nVec: Vec< &str > = aStr.split( ' ' ).clone().collect();
NOT working?

Any idea where a reference to the original comes into play in this case b?

Ahh, I understand your concern now. How about instead of taking in a borrowed value and returns a vector of &str, my_splitter takes in a borrowed value and returns a vector of owned String?

fn my_splitter(aStr: &str) -> Vec<String> {
    aStr.split_whitespace().map(|x| x.to_string()).collect()
}

Now you should be able to get rid of the .clone() in main().

EDIT: To be honest, I'm not exactly sure why something like your case b or aStr.split_whitespace().map(|x| x.clone()).collect() still raises the same error in main(). Perhaps someone else will come along and enlighten us both!

Thanks, your proposal is working well :slight_smile:

Now I need to re-think the variants we've discussed so far, in order to understand properly the key points (why [not] working) of each one.

Remark: I'm using aStr.split( ' ' ) in order to switch easily to, e.g. aStr.split( 'y' )

W.r.t. your "EDIT": The only [abstract] reason for this behaviour I can think of is:
the references themselves get cloned, not the object they are referring to...

str::split_whitespace() returns impl Iterator<Item=&str>, and str is not Clone while &str is.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.