The Error Handling chapter of the Rust Book contains an example on how to use the combinators of Option
and Result
. A file is read and through application of a series of combinators the contents are parsed as an i32
and returned in a Result<i32, String>
.
Now, I got confused when I looked at the code. There, in one closure to an and_then a local String
value is created an subsequently passed as a return value to another combinator.
Here is the code example:
use std::fs::File;
use std::io::Read;
use std::path::Path;
fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
File::open(file_path)
.map_err(|err| err.to_string())
.and_then(|mut file| {
let mut contents = String::new(); // local value
file.read_to_string(&mut contents)
.map_err(|err| err.to_string())
.map(|_| contents) // moved without 'move'
})
.and_then(|contents| {
contents.trim().parse::<i32>()
.map_err(|err| err.to_string())
})
.map(|n| 2 * n)
}
fn main() {
match file_double("foobar") {
Ok(n) => println!("{}", n),
Err(err) => println!("Error: {}", err),
}
}
The value I am referring to is contents
. It is created and later referenced in the map
combinator applied to the std::io::Result<usize>
return value of Read::read_to_string
.
The question: I thought that not marking the closure with move
would borrow any referenced value by default, which would result in the borrow checker complaining, that contents
does not live long enough. However, this code compiles just fine. That means, the String
contents
is moved into, and subequently out of, the closure. Why is this done without the explicit move
?