Ownership problem with char to string conversion

Solution 1:

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.

Solution 2:

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));
    }
}