[SOLVED] String Lifetime Error: Borrowed Value Does Not Live Long Enough


#1

I’m putting together some basic utilities for myself as I’m introducing myself to Rust, and I am implementing a function to split a string along whitespace and return the collected vector.

I’ve read over the chapters on references, borrowing, and lifetimes a couple times, but some things are still a little slow to click for me. I’m receiving a borrowed value does not live long enough error, and figured I could find some help here in the community.

Here is my module, string.rs:

extern crate core;

use self::core::str::Split;

pub fn split_on_whitespace(line: &str) -> Vec<&str> {
    let mut v: Vec<&str> = line.to_string()
                            .split(char::is_whitespace)
                            .collect();
    v
}

…and here is my main.rs:

extern crate joe_utils;

use joe_utils::string as joe_str;

fn main() {
    let test_str = "Hello World".to_string();

    let split_results = joe_str::split_on_whitespace(&test_str);

    assert_eq!(split_results, vec!["Hello", "World"]);
}

The error I am receiving is as follows:

➜  joe_utils git:(master) ✗ cargo build
   Compiling joe_utils v0.1.0 (<path omitted>/joe_utils)
src/string.rs:8:30: 8:39 error: the trait `string::core::iter::FromIterator<&str>` is not implemented for the type `collections::vec::Vec<&mut str>` [E0277]
src/string.rs:8                             .collect();
                                             ^~~~~~~~~
src/string.rs:8:30: 8:39 help: run `rustc --explain E0277` to see a detailed explanation
src/string.rs:8:30: 8:39 note: a collection of type `collections::vec::Vec<&mut str>` cannot be built from an iterator over elements of type `&str`
error: aborting due to previous error
Could not compile `joe_utils`.

To learn more, run the command again with --verbose.
➜  joe_utils git:(master) ✗ cargo build
   Compiling joe_utils v0.1.0 (<Path Omitted>/joe_utils)
src/string.rs:6:28: 6:44 error: borrowed value does not live long enough
src/string.rs:6     let mut v: Vec<&str> = line.to_string()
                                           ^~~~~~~~~~~~~~~~
src/string.rs:5:57: 10:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 5:56...
src/string.rs: 5 pub fn split_on_whitespace(line: &mut str) -> Vec<&str> {
src/string.rs: 6     let mut v: Vec<&str> = line.to_string()
src/string.rs: 7                             .split(char::is_whitespace)
src/string.rs: 8                             .collect();
src/string.rs: 9     v
src/string.rs:10 }
src/string.rs:6:5: 8:40 note: ...but borrowed value is only valid for the statement at 6:4
src/string.rs:6     let mut v: Vec<&str> = line.to_string()
src/string.rs:7                             .split(char::is_whitespace)
src/string.rs:8                             .collect();
src/string.rs:6:5: 8:40 help: consider using a `let` binding to increase its lifetime
src/string.rs:6     let mut v: Vec<&str> = line.to_string()
src/string.rs:7                             .split(char::is_whitespace)
src/string.rs:8                             .collect();
error: aborting due to previous error
Could not compile `joe_utils`.

I’ve tried a couple variations of mutable/immutable on the string coming in and splitting the steps up and using let bindings instead of chaining them inside the function, but I get the same type of error every time so I think I’m missing something fundamental.

I know it’s a pretty simple issue, but I just need another set of eyes right now–any help or even a point in the right direction would be greatly appreciated. Thanks!


#2

The culprit is the .to_string() in your split_on_whitespace. If you take it out, the function compiles, since you’re returning a vector containing parts of the slice you passed in. With the .to_string() you create a new String in your function, that will be destroyed before the function returns, so you can’t return slices pointing to that.


#3

That did the trick! Thank you @phaylon for your help and the quick reply!

After the fact I also realized that str has a method split_whitespace() that does what I did with split(char::is_whitespace) above.

My function ended up being:

pub fn split_on_whitespace(line: &str) -> Vec<&str> {
    let split = line.split_whitespace();
    let v: Vec<&str> = split.collect();
    v
}

#4

If you’re wondering, that code can be reduced to this, because rust’s local type inference is excellent:

pub fn split_on_whitespace(line: &str) -> Vec<&str> {
    line.split_whitespace().collect()
}