How to remove immutable borrow variable?

I'm studying Rust to follow the book.

there is an example about string slice.

fn main() {
  let s = String::from("Hello World!");

  let word = first_word(&s);
  s.clear(); // Error!
  println!("first word is {}", word);
}

on the above program, what if I would like to clear s, then how can I remove word, immutable borrow?

Your word is holding a reference to data inside s. Use to_owned or to_string to allocate a copy and cut the ties that bind word and s.

fn main() {
  let mut s = String::from("Hello World!");

  // let word : &str = first_word(&s);
  let word : String = first_word(&s).to_string();
  s.clear();
  println!("first word is {}", word);
}

fn first_word(s: &String) -> &str {
    s.split_whitespace().next().unwrap()
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e597288a96fe0203f7b3290090d5edfb

For other data types, .clone() may be sufficient to release an immutable borrow. It depends on how the type was designed. For &str, .clone() preserves the borrow, which is why .to_string() is used in that example.

1 Like

Thanks for the perfect solution!

then, isn't there any way to remove immutable borrowed variable once I borrow immutably?

No, doing so could make the immutable borrow dangling. For example, let's say first_word returns a sub-string of the input. Then s.clear() would clear the string, and word would be pointing into uninitialized memory.

1 Like

I think you're asking if you can throw away the variable holding the immutable borrow. Sure, use a scope boundary (which I normally use), or possibly even drop(). You cannot throw away s before word, since s owns the allocated memory that word borrows from, so these two examples use and drop word before s.clear() is used.

fn main() {
  let mut s = String::from("Hello World!");

  {
      let word = first_word(&s);
      println!("first word is {}", word);
      // Variable word dropped at end of scope, and
      // its immutable borrow is released.
  }
  s.clear();
}

fn first_word(s: &String) -> &str {
    s.split_whitespace().next().unwrap()
}

Playground using scope boundary

fn main() {
  let mut s = String::from("Hello World!");

  let word = first_word(&s);
  println!("first word is {}", word);
  // Variable word dropped, and
  // its immutable borrow is released.
  drop(word);
  
  s.clear();
}

fn first_word(s: &String) -> &str {
    s.split_whitespace().next().unwrap()
}

Playground using drop

1 Like

sweet. I didn't know to call drop manually. thanks!

This doesn't do anything. Delete it and the example will still compile, add a use of word after the s.clear() and it won't.

A reference disappears as soon as you stop using it, so there no need to explicitly remove one. Also shared reference are copy so attempting to drop one doesn't do anything.

4 Likes