2

My failing code (Minimal, Reproducible Example):

// my code

fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i].to_string()));
    }
}

// external crate

#[derive(Debug, Clone, Copy, PartialEq)]
struct Text<'a> {
    pub text: &'a str,
}

impl<'a> Text<'a> {
    pub fn new(text: &'a str) -> Self {
        Text {
            text
        }
    }
}

The compiler: "temporary value dropped while borrowed". Red squiggly lines below

ARRAY[i].to_string()

Classic borrow checker problem, I guess?

I tried to change up the types to be &strs instead of chars and everything worked fine:

// my code
fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [&str; 3] = ["a", "b", "c"]; // CHANGE HERE
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i])); // CHANGE HERE
    }
}

Can't figure out what's so special about char or the conversion with to_string() that makes this code fail.

E_net4
  • 27,810
  • 13
  • 101
  • 139
balanceglove2
  • 398
  • 5
  • 12
  • Does this answer your question? [Why Is Temporary Value Dropped While Borrowed An Issue?](https://stackoverflow.com/questions/67407567/why-is-temporary-value-dropped-while-borrowed-an-issue) – E_net4 Jan 17 '22 at 11:42
  • 1
    @E_net4isnotinaforum While the borrow checker issue in the two question is the same, outside constraints are different, so the approach recommended by the answer to the other question probably won't help here. In this question the type that holds the references (`Text`) is provided externally and the OP cannot switch it to owned data. – user4815162342 Jan 17 '22 at 13:04

2 Answers2

5

Your failing code is equivalent to this one:

for a in &ARRAY {
    let temp = a.to_string();
    list.push(Text::new(&temp));
    drop(temp);
}

Except that the temporary variable has a name in my code (the call to drop() is redundant, I added it for clarity).

So all the temporaries created during the loop are being added as references to the list, remember that Text holds a reference, not the string itself. But these temporaries do not survive the iteration where they are created, so your list contains dangling references. Rust detects that and fails to compile.

The easy solution would be to modify Text to hold the String itself, instead of a reference. If you cannot modify that, then your strings have to live somewhere, you can build a Vec<String> and get the references from there:

fn test() {
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    let mut tmps: Vec<String> = Vec::new();
    for a in &ARRAY {
        tmps.push(a.to_string());
    }
    let list: Vec<Text> = tmps
        .iter()
        .map(|s| Text::new(s))
        .collect();
}

Your second code works simply because now you array contains static references &'static str so there is no temporary variables anywhere.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Thanks a lot! I used @user4815162342's code (although it's probably the same) but your explanation was very insightful. Thanks! – balanceglove2 Jan 17 '22 at 11:56
4

What's special is that to_string() creates an owned string, whereas "..." gives you a &'static str. The owned string has a clear life cycle which ends at the end of its scope, when it deallocates the memory where the data is stored. You attempt to store references to such strings and hold on to them after the string has already been deallocated.

The Text type from the external crate is designed to borrow data owned by someone else. If you can't change that, you'll simply need to make sure that you keep the owned values around for at least as long as the Text values. For example:

fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    // keep the strings live
    let strings: Vec<String> = ARRAY.iter().map(|c| c.to_string()).collect();
    // `Text`s in `list` refer to strings in `strings`
    for s in &strings {
        list.push(Text::new(s));
    }
}
user4815162342
  • 141,790
  • 18
  • 296
  • 355