Why does File::open move the string here?

Why doesn't it just borrow the string? If I make it &thefile, it works, but I don't understand how it can move from a non-reference argument but it can't borrow.

use std::fs::File;

fn main() {
    let thefile = String::from("the name");
    let mut handle = File::open(thefile).expect("Couldn't open it!");
    println!("{}", thefile);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `handle`
 --> src/lib.rs:5:13
  |
5 |     let mut handle = File::open(thefile).expect("Couldn't open it!");
  |             ^^^^^^ help: if this is intentional, prefix it with an underscore: `_handle`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: variable does not need to be mutable
 --> src/lib.rs:5:9
  |
5 |     let mut handle = File::open(thefile).expect("Couldn't open it!");
  |         ----^^^^^^
  |         |
  |         help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` on by default

error[E0382]: borrow of moved value: `thefile`
 --> src/lib.rs:6:20
  |
4 |     let thefile = String::from("the name");
  |         ------- move occurs because `thefile` has type `String`, which does not implement the `Copy` trait
5 |     let mut handle = File::open(thefile).expect("Couldn't open it!");
  |                                 ------- value moved here
6 |     println!("{}", thefile);
  |                    ^^^^^^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0382`.
warning: `playground` (lib) generated 2 warnings
error: could not compile `playground` due to previous error; 2 warnings emitted

The function is generic over any type that implements AsRef<Path>. Both String and &str implement that trait, but if you choose to pass a String, you give up ownership.

It's generic over this trait because otherwise you wouldn't be able to use &str as argument either. It would have to be a &Path only.

4 Likes

Rust has no implicit pass-by-reference semantics, so calling a function always moves its arguments. In this case, File::open(thefile) moves thefile into the function, and we can't use it again since String is not Copy. But if we call File::open(&thefile), we only move a &String reference into the function, so we can continue to access thefile once the function returns.

4 Likes