我正在尝试编写一个函数,它将封装一系列链接的迭代器方法调用(.lines().map(...).filter(...)
),这些调用目前已经重复了。我无法弄清楚要编译的类型签名。如果这对Rust来说不可能或非常单一,那么我愿意接受惯用法的建议。
use std::fs;
use std::io;
use std::io::prelude::*;
use std::iter;
const WORDS_PATH: &str = "/usr/share/dict/words";
fn is_short(word: &String) -> bool {
word.len() < 7
}
fn unwrap(result: Result<String, io::Error>) -> String {
result.unwrap()
}
fn main_works_but_code_dupe() {
let file = fs::File::open(WORDS_PATH).unwrap();
let reader = io::BufReader::new(&file);
let count = reader.lines().map(unwrap).filter(is_short).count();
println!("{:?}", count);
let mut reader = io::BufReader::new(&file);
reader.seek(io::SeekFrom::Start(0));
let sample_size = (0.05 * count as f32) as usize; // 5% sample
// This chain of iterator logic is duplicated
for line in reader.lines().map(unwrap).filter(is_short).take(sample_size) {
println!("{}", line);
}
}
fn short_lines<'a, T>
(reader: &'a T)
-> iter::Filter<std::iter::Map<std::io::Lines<T>, &FnMut(&str, bool)>, &FnMut(&str, bool)>
where T: io::BufRead
{
reader.lines().map(unwrap).filter(is_short)
}
fn main_dry() {
let file = fs::File::open(WORDS_PATH).unwrap();
let reader = io::BufReader::new(&file);
let count = short_lines(reader).count();
println!("{:?}", count);
// Would like to do this instead:
let mut reader = io::BufReader::new(&file);
reader.seek(io::SeekFrom::Start(0));
let sample_size = (0.05 * count as f32) as usize; // 5% sample
for line in short_lines(reader).take(sample_size) {
println!("{}", line);
}
}
fn main() {
main_works_but_code_dupe();
}
答案 0 :(得分:5)
我无法弄清楚要编译的类型签名。
编译器告诉你它是什么。
error[E0308]: mismatched types
--> src/main.rs:35:5
|
35 | reader.lines().map(unwrap).filter(is_short)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found fn item
|
= note: expected type `std::iter::Filter<std::iter::Map<_, &'a for<'r> std::ops::FnMut(&'r str, bool) + 'a>, &'a for<'r> std::ops::FnMut(&'r str, bool) + 'a>`
found type `std::iter::Filter<std::iter::Map<_, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String {unwrap}>, for<'r> fn(&'r std::string::String) -> bool {is_short}>`
现在,获得批准,你不能直接复制+粘贴它。您必须将_
类型替换为您已经拥有的实际类型(由于它已经正确,因此将其删除)。其次,您需要删除{unwrap}
和{is_short}
位;那些是因为函数项具有唯一类型,这就是编译器对它们进行注释的方式。可悲的是,你实际上不能写这些类型。
重新编译并......
error[E0308]: mismatched types
--> src/main.rs:35:5
|
32 | -> std::iter::Filter<std::iter::Map<std::io::Lines<T>, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String>, for<'r> fn(&'r std::string::String) -> bool>
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- expected `std::iter::Filter<std::iter::Map<std::io::Lines<T>, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String>, for<'r> fn(&'r std::string::String) -> bool>` because of return type
...
35 | reader.lines().map(unwrap).filter(is_short)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found fn item
|
= note: expected type `std::iter::Filter<std::iter::Map<_, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String>, for<'r> fn(&'r std::string::String) -> bool>`
found type `std::iter::Filter<std::iter::Map<_, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String {unwrap}>, for<'r> fn(&'r std::string::String) -> bool {is_short}>`
还记得我所说的功能项有什么独特的类型吗?是的,那。要修复那个,我们从函数项转换为函数指针。我们甚至不需要指定我们要投射的内容,我们只需让编译器知道我们希望它进行投射。
fn short_lines<'a, T>
(reader: &'a T)
-> std::iter::Filter<std::iter::Map<std::io::Lines<T>, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String>, for<'r> fn(&'r std::string::String) -> bool>
where T: io::BufRead
{
reader.lines().map(unwrap as _).filter(is_short as _)
}
error[E0308]: mismatched types
--> src/main.rs:41:29
|
41 | let count = short_lines(reader).count();
| ^^^^^^ expected reference, found struct `std::io::BufReader`
|
= note: expected type `&_`
found type `std::io::BufReader<&std::fs::File>`
= help: try with `&reader`
同样,编译器会告诉您确切要做什么。做出改变......
error[E0507]: cannot move out of borrowed content
--> src/main.rs:35:5
|
35 | reader.lines().map(unwrap as _).filter(is_short as _)
| ^^^^^^ cannot move out of borrowed content
是的,那是因为short_lines
的输入错了。还有一个变化:
fn short_lines<T>
(reader: T)
-> std::iter::Filter<std::iter::Map<std::io::Lines<T>, fn(std::result::Result<std::string::String, std::io::Error>) -> std::string::String>, for<'r> fn(&'r std::string::String) -> bool>
where T: io::BufRead
{
reader.lines().map(unwrap as _).filter(is_short as _)
}
现在你需要处理的只是警告。
简而言之:阅读编译器消息。它们很有用。
答案 1 :(得分:3)
我建议以简单的方式完成它 - 将迭代器收集到一个向量中:
use std::fs;
use std::io;
use std::io::prelude::*;
const WORDS_PATH: &str = "/usr/share/dict/words";
fn main() {
let file = fs::File::open(WORDS_PATH).unwrap();
let reader = io::BufReader::new(&file);
let short_lines = reader.lines()
.map(|l| l.unwrap())
.filter(|l| l.len() < 7)
.collect::<Vec<_>>(); // the element type can just be inferred
let count = short_lines.len();
println!("{:?}", count);
let sample_size = (0.05 * count as f32) as usize; // 5% sample
for line in &short_lines[0..sample_size] {
println!("{}", line);
}
}