提取迭代器链调用辅助函数

Pet*_*ons 2 iterator rust

我正在尝试编写一个函数,它将封装一系列链接的迭代器方法调用(.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();
}
Run Code Online (Sandbox Code Playgroud)

DK.*_*DK. 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}>`
Run Code Online (Sandbox Code Playgroud)

现在,被授予,你不能直接复制+粘贴它.你必须_用你已经拥有的实际替换类型(因为它已经是正确的,所以它没有了).其次,你需要删除{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}>`
Run Code Online (Sandbox Code Playgroud)

还记得我说的功能项有什么独特的类型吗?是的,那.为了解决这个问题,我们从函数项转换为函数指针.我们甚至不需要指定我们要转换的内容,我们只需让编译器知道我们希望它进行转换.

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 _)
}
Run Code Online (Sandbox Code Playgroud)
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`
Run Code Online (Sandbox Code Playgroud)

同样,编译器会告诉您确切要做什么.做出改变......

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
Run Code Online (Sandbox Code Playgroud)

对,这是因为你得到了输入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 _)
}
Run Code Online (Sandbox Code Playgroud)

而现在你所要处理的只是警告.

简而言之:读取编译器消息.它们很有用.

  • 谢谢你!请考虑我正在学习生锈和长复杂类型签名对我来说是新的,只是因为我基本上理解"类型错误"我不一定理解100字符类型签名以及如何有意义地解释编译器消息. (2认同)