Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shadow variable not working in Rust, gives lifetime errors

Tags:

rust

Here is my snippet:

pub fn abbreviate(mut phrase: &str) -> String {
    let mut tla = String::new();
    phrase = phrase.replace("-", "");
}

Gives this error

1 | pub fn abbreviate(mut phrase: &str) -> String {
  |                               ---- expected due to this parameter type
...
4 |     phrase = phrase.replace("-", "");
  |              ^^^^^^^^^^^^^^^^^^^^^^^
  |              |
  |              expected `&str`, found struct `String`
  |              help: consider borrowing here: `&phrase.replace("-", "")`

if I follow the suggestion

pub fn abbreviate(mut phrase: &str) -> String {
    let mut tla = String::new();
    phrase = &phrase.replace("-", "");
}

I get this lifetime compile error

1 | pub fn abbreviate(mut phrase: &str) -> String {
  |                               - let's call the lifetime of this reference `'1`
...
4 |     phrase = &phrase.replace("-", "");
  |     ----------^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
  |     |         |
  |     |         creates a temporary which is freed while still in use
  |     assignment requires that borrow lasts for `'1`

This works but is ugly:

pub fn abbreviate(mut phrase: &str) -> String {
    let mut tla = String::new();
    let phrase2 = &phrase.replace("-", "");
}

Why can I not shawdow the phrase variable? Can somebody please explain the above compiler error about lifetimes?

like image 999
Nate Houk Avatar asked Mar 23 '26 10:03

Nate Houk


1 Answers

A &str is a string slice that refers to data held elsewhere. In the case of a string literal, you have a &'static str which is a reference to data held in the binary itself. Otherwise, you typically produce a &str by taking a slice of an existing String.

If you are generating a new string at runtime, the most straightforward way is to make a String. You can then dispense slices (&str) from it, but the String value must itself live at least as long as the slices. &phrase.replace("-", "") doesn't work here because you're taking a slice of a temporary -- a value that only lives as long as the statement it's part of. After the end of this statement, the temporary String returned by the replace method is dropped and phrase would refer to a slice that points into a string that no longer exists, and the borrow checker is correctly pointing out that this is not memory-safe. It would be like returning a reference to a value held in a local variable within the same function.

However, what you're doing here isn't even shadowing, you're just trying to reassign a variable from an incompatible type. Shadowing the name phrase with a new variable of the same name will allow the new name to have a different type (such as String), so this would be valid:

pub fn abbreviate(phrase: &str) -> String {
    let mut tla = String::new();
    // phrase refers to a &str here
    let phrase = phrase.replace("-", "");
    // phrase refers to a String here

    // Return something
}

Basically, your first example only fails because you didn't put let before the assignment.

Note also that the phrase argument doesn't need to be mut here. let phrase introduces a new variable with the same name, effectively hiding the argument from the rest of the function. It doesn't actually change the value held in the original name.

like image 104
cdhowie Avatar answered Mar 26 '26 00:03

cdhowie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!