0

I use rust to open a file and parse its contents.

let file = fs::read_to_string(file_path).unwrap(); 

I then iterate over each line:

for item in file.lines() {
// processing
}

at this point item is of type &str. I now need only a slice of that so I:

let string_slice = &item[0..2];

however, string_slice is now of type &&str (two &s). I want to return a vec of strings, however the file I read doesnt need to live longer than the function scope I open it in. I can get to a string by calling to_string() twice; but this seems awkward.

let string_slice = &item[0..2].to_string();
return string_slice.to_string() //have to call twice

is there a more elegant way to pass ownership of these slices to a vec that I then return from the function?

8
  • In the description you say that the return type is Vec<String>, but the code snippet contains return string_slice.to_string() which returns a single String. Which one do you want? Commented Feb 1, 2023 at 9:58
  • it doesn't matter. in the end i want a string. the only difference would be return vec![string_slice.to_string()]. Commented Feb 1, 2023 at 9:59
  • 1
    The type of string_slice will be &str, not &&str. And even if it were &&str, a single call to to_string() would be enough to turn it into a String. Commented Feb 1, 2023 at 10:00
  • 1
    i found a better solution, which is to (&item[0..2]).to_string() -> returns a String. i think the order of operations without the () is different thats what caused me problems Commented Feb 1, 2023 at 10:00
  • 2
    Or just item[0..2].to_string(). Commented Feb 1, 2023 at 10:01

2 Answers 2

3

The other answer explains what happens under the hood, but if you want to know the idiomatic way to achieve what you're trying to, this is it:

fn get_lines() -> Vec<String> {
    // Replace this with your actual input. You can `expect()` the value
    // (instead of `unwrap()`) if you're sure it's there or return a `Result`
    // if you're not.
    // You can also pass it in as an argument. In that case, the parameter type
    // should be `&str` instead of `String`. This is a common mistake among
    // beginners.
    let sample_input = String::from("one\ntwo\nthree\n");

    sample_input
        .lines()
        // You can use `to_string()`, `to_owned()`, or `into()` to convert a
        // string slice into a string. Either works. I find the automatic type
        // inference of `into()` most satisfying, but it's up to preference in
        // this case.
        // The type annotation for `line` is optional.
        .map(|line: &str| line[0..2].into())
        // Because the function returns a `Vec`, you can omit the type. I.e.,
        // you don't have to write `collect::<Vec<_>>()`. It's inferred.
        .collect()
}

Run this snippet on Rust Playground.

Sign up to request clarification or add additional context in comments.

Comments

2
let string_slice: &str = &item[0..2];

however, string_slice is now of type &&str (two &s).

That's not right, the type of item[0..2] is str so you have to take a reference to use it since it's unsized, the type of &item[0..2] is then the &str you can work with.

When you directly invoke a method on it like here:

let string_slice: &String = &item[0..2].to_string();

due to auto (de-)referencing the compiler will add an automatic reference to the string slice so it desugars to this:

let string_slice: &String = &( // <- the referen you take explicitly
    /*the reference the compiler inserts -> */ (&item[0..2]).to_string()
);

I added an extra pair of parentheses to make the precedence clear. So after this string_slice which is a bit of a misnomer here is of type &String. To get a String instead you can just remove your manual reference.

let string_slice: String = item[0..2].to_string();

which desugars to

let string_slice: String = (&item[0..2]).to_string();

I've annotated all the types above for ease of understanding, but they're exactly what the compiler would infer for them.


As a tip to get the compiler to tell you what type a variable is if you don't have editor support which can tell you you can always add a type annotation of : () and the compiler will error with a message telling you what it expected the type to be:

let string_slice: () = &item[0..2];

will error with something like the following

error[E0308]: mismatched types
 --> src/main.rs:2:17
  |
  |     let string_slice: () = &item[..];
  |                       --   ^^^^^^^^^ expected `()`, found `&str`
  |                       |
  |                       expected due to this

For more information about this error, try `rustc --explain E0308`.

1 Comment

thanks for your answer. it took me a while to understand the dereferencing of &item[0..2].to_string(), but im glad you explaned it. cheers

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.