Borrowing Confusion

let mut request = String::new();
let mut tmp = String::new();
let mut params = HashMap::new();

//I then have tmp and request vars getting set with format! macro in a for loop
//upon hitting a certain field I'm trying to write those vars into a hashmap with this

params.insert( &tmp, &request );

if I leave off the & it says that I'm trying to pass String and need str and suggests adding &
however, if I add & I then start getting errors about borrowing of these 2 vars throughout my
code that use to work just fine before adding this line.

I'm a little stuck and still trying to get my head wrapped around all the borrowing stuff.

Thanks,

Glenn

Most likely you want HasMap owning content so have key and value as String. i.e. no refs to insert. Then have to fix rest of code to accept it.

fn main() {
    let request = String::new();
    let tmp = String::new();
    let mut params = std::collections::HashMap::new();

    params.insert(request, tmp);
    
}


^-- this appears to compile

I suspect what it's trying to say is that you're trying to move the variables inside a loop.

Without the &, you're moving the strings inside the hashmap. This is a problem because you're trying to use them again in the next loop, but they would no longer be available. You can fix this among other methods by cloning the strings and putting the clones into the hashmap.

With the &, you're making the hashmap borrow the strings. This is a problem because you're trying to reassign the variables in the next loop. You can't do that while they are being borrowed. You probably don't want to put borrowed strings in the hashmap in this case.

As an aside, posting the full error messages and a small code example that demonstrates the issue is helpful, I'm not 100% sure I interpreted the error you're getting correctly.

1 Like

That doesn't seem to work inside a for loop.

  let mut counter = 0;                                                                            
  let mut request = String::new();                                                                
  let mut tmp = String::new();                                                                    
  let mut params = std::collections::HashMap::new();                                              
                                         
  params.insert( "key","hash" );                                                                  
  params.insert( "hostname","host" );                                                             
                                                                                                                                           
  for line in reader.lines()                                                                      
      {                                                                                           
      let st: String = line.unwrap();                                                             
      if st == "$V 4.0" { continue }                                                              
                                            
      if st == "$T P"                                                                             
          {                                                                                       
          println!( "We don't support importing pricefiles through interface" );                  
          drop( inputfile );                                                                      
          fs::remove_file( inputfile );                                                           
          return;                                                                                 
          }                                                                                       
                                                                                                  
      if st != "0" && st.len() > 0                                                                
          {                                                                                       
          let recordkey:String = st[0..1].to_string();                                            
                                                                                                  
          if newrecord( recordkey )                                                               
              {                                                                                   
              if counter > 0                                                                      
                  {                                                                               
                  params.insert( &tmp, &request );                                                                             
                                                                                                                                   
                  request = String::from("");                                                                                      
                  }                                                                                                                
                                                                                                                                   
              counter += 1;                                                                                                        
              tmp = format!("record{}", counter);                                                                                  
              }                                                                                                                    
                                                                                                                                   
          request = format!( "{}|{}",request, st );                                                                                
          }                                                                                       
      }
Compiling psl v0.1.0 (/home/ghancock/source/rust/psl)
error[E0506]: cannot assign to `request` because it is borrowed
 --> src/main.rs:79:6
 |
76 |                     params.insert( &tmp, &request );
  |                                          -------- borrow of `request` occurs here
...
79 |                     request = String::from("");
   |                     ^^^^^^^ assignment to borrowed `request` occurs here
...
90 |     println!( "{:?}", params );
   |                       ------ borrow later used here

error[E0506]: cannot assign to `tmp` because it is borrowed
  --> src/main.rs:83:5
   |
76 |                     params.insert( &tmp, &request );
   |                                    ---- borrow of `tmp` occurs here
...
83 |                 tmp = format!("record{}", counter);
   |                 ^^^ assignment to borrowed `tmp` occurs here
...
90 |     println!( "{:?}", params );
   |                       ------ borrow later used here

error[E0506]: cannot assign to `request` because it is borrowed
 --> src/main.rs:86:4
   |
76 |                     params.insert( &tmp, &request );
   |                                          -------- borrow of `request` occurs here
...
86 |             request = format!( "{}|{}",request, st );
 |             ^^^^^^^ assignment to borrowed `request` occurs here
...
90 |     println!( "{:?}", params );
 |                       ------ borrow later used here

This is not recommended in general, but for this particular case, can you get the code to compile by changing all instances of

.insert(&x, &y)

into

.insert(x.clone(), y.clone())

This is not good practice in general, but for now, just to see if there is any issues besides borrowing.

get x and y are wrong type. looking for &str but seeing String

Also change the "abc" into String::from("abc")

Also change that to:

let mut params = std::collections::HashMap::<String, String>::new();

Making progress. I changed the collection type to be 2 strings which took care of the insert problem. I then had to modify the host and key types (which I noticed the source was wrong for them anyway) so that they would insert onto hashmap correctly.

I have the program compiling again but still trying to get my head wrapped around all this. Is it normal to get so bogged down in this borrowing thing? I think it is on the surface easy enough to explain but implementation wise is giving me fits.

ie: I tried to do the following before I made above changes:
let my_tmp = &tmp;

Which I thought would give me a new var my_tmp linked to the original but when I compiled it said tmp was borrowed on that line. Hopefully I'll get this settled in my tiny brain soon.

Thanks for the help guys,

Glenn

It is entirely possible I have the wrong philosophy here, but I see it as:

reading about borrow checker :: getting code to compile = reading books about basketball :: playing basketball

It is definitely important to read about lifetime / borrowing, but I found it much easier to understand once I had concrete examples of "Code XYZ gave compiler error FOO. We fixed it by transform BAR."

3 Likes

I like to think of a HashMap as a cupboard where you keep your coffee mugs. The mugs are the items in the map, the names printed on the mugs are the keys.

When you put a coffee mug in the cupboard you no longer have it in your hand. The mug has moved into the cupboard. When you take a coffee mug out of the cupboard it is no longer in the cupboard. The mug has moved to your hand.

The mug can only be in one place at one time. If you want "Dave's" mug to be in the cupboard and in you hand at the same time Dave had better have two mugs. There will have to be a clone.

In Rust speak, either you or the cupboard "owns" the mug.

"Ah", you say, "my mugs are very expensive to clone, I want to keep them outside the cupboard and just have labels (references) in the cupboard with the names on them and an indication of where the mug it names is."

That might work. Until you drop and smash a coffee mug one day. Now the mug does not exist but the label in the cupboard does. Dave get's angry when he can't find his mug were the label in the cupboard says it is.

If you really want references in the cupboard then use smart pointers that know to keep the mugs in existence as long as there is at least one reference to it.

3 Likes

As my experience, suggestion is not error. For more type information take a look this code

fn main() {
    let s = String::from("Hello world");    // This type is String
    let s1 = &s;    // This type is just &String, not &str

    let slice = &s[0..5]; // This is &str type

    println!("{}", slice);
}

If you take a reference of a String you will get &String and &str is string slice which is called as string literal.

First you don't need mut in tmp and request. As in your code if you insert params.insert( &tmp, &request ); like this your type information of parms will be HashMap<&String, &String>. If you insert as params.insert( tmp, request ); the parms will be HashMap<String, String> and still compile. But you can't use tmp and request from this point because the value is moved into the hashmap. To make sure I suggest that when you declare the HashMap you should add type information as your wish like this let mut parms: HashMap<String, String> = HashMap::new();.