I have this code:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
file.lines().map(|x| x.unwrap()).collect();
}
fn main() {
let data = load_file();
println!("DATA: {}", data[0]);
}
When I try to compile it, I get this error:
error[E0283]: type annotations required: cannot resolve `_: std::iter::FromIterator<std::string::String>`
--> src/main.rs:6:38
|
6 | file.lines().map(|x| x.unwrap()).collect();
| ^^^^^^^
In fact, if I change the load_file
function in this way, the code compiles smoothly:
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
return lines;
}
This solution is not "Rusty" enough because ending a function with a return is not encouraged.
Is there a way to put the type annotation directly into the file.lines().map(|x| x.unwrap()).collect();
statement?
Iterator::collect
's signature looks like this:
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
In your case, you need to tell it what B
is. To specify the types of a generic function, you use syntax called the turbofish, which looks like func::<T, U, ...>()
Your load_file
function should look like this:
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
file.lines().map(|x| x.unwrap()).collect::<Vec<String>>()
}
You can also allow some type inference to continue by specifying some types as the placeholder _
:
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
file.lines().map(|x| x.unwrap()).collect::<Vec<_>>()
}
In fact your problem was slightly less noticeable. This does not compile (your initial piece of code):
use std::fs::File;
use std::io::{BufRead, BufReader};
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
file.lines().map(|x| x.unwrap()).collect();
}
fn main() {
let data = load_file();
println!("DATA: {}", data[0]);
}
But this does:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn load_file() -> Vec<String> {
let file = BufReader::new(File::open("foo.txt").unwrap());
file.lines().map(|x| x.unwrap()).collect()
}
fn main() {
let data = load_file();
println!("DATA: {}", data[0]);
}
Can you notice the subtle difference? It's just a semicolon in the last line of load_file()
.
Type inference in Rust is strong enough not to need an annotation here. Your problem was in that you was ignoring the result of collect()
! The semicolon acted like a "barrier" for the type inference, because with it collect()
's return type and load_file()
's return type are not connected. The error message is somewhat misleading, however; it seems that this phase of type checking ran earlier than the check for return types (which would rightly fail because ()
is not compatible with Vec<String>
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With