1

I have a function that should read a file and returns it's contents.

fn read (file_name: &str) -> &str {

    let mut f = File::open(file_name)
        .expect(&format!("file not found: {}", file_name));

    let mut contents = String::new();

    f.read_to_string(&mut contents)
        .expect(&format!("cannot read file {}", file_name));

    return &contents;
}

But I get this error:

  --> src\main.rs:20:13
   |
20 |     return &contents;
   |             ^^^^^^^^ borrowed value does not live long enough
21 | }
   | - borrowed value only lives until here
   |

What am I doing wrong?

My Idea of what is happening here is this:

  1. let mut f = File::open(file_name).expect(....); - this takes a handle of a file and tells the OS that we want to do things with it.

  2. let mut contents = String::new(); - this creates a vector-like data structure on the heap in order to store the data that we are about to read from the file.

  3. f.read_to_string(&mut contents).expect(...); - this reads the file into the contents space.

  4. return &contents; - this returns a pointer to the vector where the file data is stored.

Why am I not able to return the pointer that I want?

How do I close my file (the f variable)? I think that rust will close it for me after the variable goes out of scope, but what If I need to close it before that?

Hristo Kolev
  • 1,486
  • 1
  • 16
  • 33

1 Answers1

2

You are correct about the file handle being closed automatically when its variable goes out of scope; the same will happen to contents, though - it will be destroyed at the end of the function, unless you decide to return it as an owned String. In Rust functions can't return references to objects created inside them, only to those passed to them as arguments.

You can fix your function as follows:

fn read(file_name: &str) -> String {
    let mut f = File::open(file_name)
        .expect(&format!("file not found: {}", file_name));

    let mut contents = String::new();

    f.read_to_string(&mut contents)
        .expect(&format!("cannot read file {}", file_name));

    contents
}

Alternatively, you can pass contents as a mutable reference to the read function:

fn read(file_name: &str, contents: &mut String) { ... }
ljedrz
  • 20,316
  • 4
  • 69
  • 97
  • If you return the `String` from the function instead of a pointer does that mean that you are copying it in memory? – Hristo Kolev Jul 04 '18 at 18:46
  • @HristoKolev: the specifics behind move semantics are [a separate topic](https://stackoverflow.com/questions/29490670/how-does-rust-provide-move-semantics) and potential LLVM optimizations are [a whole different story](https://stackoverflow.com/questions/38571270/can-rust-optimise-away-the-bit-wise-copy-during-move-of-an-object-someday?rq=1) but, in general (outside of some `unsafe` magic perhaps), this is the way it is done in Rust. – ljedrz Jul 04 '18 at 18:53
  • But If I pass it as a `mut` parameter it doesn't do any copying? – Hristo Kolev Jul 04 '18 at 18:58
  • @HristoKolev: in general it *shouldn't* - in most cases this is expected to be optimized away. You can always verify it with addresses like in [this question](https://stackoverflow.com/questions/38571270/can-rust-optimise-away-the-bit-wise-copy-during-move-of-an-object-someday?rq=1) if you like. – ljedrz Jul 04 '18 at 19:03
  • 1
    Note that if you return the `String` from the function, it will probably copy the `String` **struct** (ie. a pointer and an int), but it won't copy the string **data** which is allocated on the heap and referenced by the pointer. – Jmb Jul 05 '18 at 06:30