Использование Map в качестве итератора взаимозаменяемо с векторным итератором

#rust

#Ржавчина

Вопрос:

Простая программа, которую я пытаюсь сделать, это получить строки из файла или, если файл отсутствует, передать итератор нулевой длины:

 use std::fs::File;
use std::io::{self, BufRead};

fn run_against_input(inp: amp;mut dyn Iterator<Item = String>) {
    for i in inp {
        println!("Input line: {}", i);
    }
}

fn main() {
    let file = File::open("data.txt");
    let input: dyn Iterator<Item = String> = match file {
        Ok(f) => io::BufReader::new(f).lines()
            .map(|line| line.unwrap()),

        Err(_) => Vec::new().into_iter()
    };
    
    run_against_input(amp;mut dyn input);
}
 

Когда я делаю это, я получаю следующую ошибку:

    Compiling playground v0.0.1 (/playground)
error: expected expression, found keyword `dyn`
  --> src/main.rs:19:28
   |
19 |     run_against_input(amp;mut dyn input);
   |                            ^^^ expected expression

error[E0308]: mismatched types
  --> src/main.rs:13:18
   |
13 |           Ok(f) => io::BufReader::new(f).lines()
   |  __________________^
14 | |             .map(|line| line.unwrap()),
   | |______________________________________^ expected trait object `dyn Iterator`, found struct `Map`
   |
   = note: expected trait object `dyn Iterator<Item = String>`
                    found struct `Map<std::io::Lines<BufReader<File>>, [closure@src/main.rs:14:18: 14:38]>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors
 

Мне действительно нужна amp;mut ссылка на Iterator в коде, который я пишу (это небольшое воспроизведение проблемы, с которой я сталкиваюсь), но я думаю, что это не имеет никакого отношения к рассматриваемой проблеме. Я вижу, что есть impl on Iterator для Map , но ошибка, которую я получаю, заключается в том, что это не объект trait для Iterator<Item = String> . Я также пробовал это:

 let input: amp;mut dyn Iterator<Item = String> = match file {
    Ok(f) => amp;mut io::BufReader::new(f).lines()
        .map(|line| line.unwrap()),

    Err(_) => amp;mut Vec::new().into_iter()
};
 

что, конечно, не сработало, поскольку временное значение отбрасывается в инструкции, на которую я возвращаю ссылку (именно поэтому я сделал let привязку, как предложил компилятор).

РЕДАКТИРОВАТЬ ссылку на игровую площадку — https://play.rust-lang.org/?version=stableamp;mode=debugamp;edition=2021amp;gist=99b8105ecc266e03db2d92cc22610962

Комментарии:

1. зачем усложнять свою жизнь с дином

Ответ №1:

Вы можете хранить итераторы для каждой ветви в отдельной переменной вне match (а также использовать std::iter::empty() вместо Vec::new().into_iter() ):

 use std::fs::File;
use std::io::{self, BufRead};
use std::iter;

fn run_against_input(inp: impl Iterator<Item = String>) {
    for i in inp {
        println!("Input line: {}", i);
    }
}

fn main() {
    let file = File::open("data.txt");
    let mut lines;
    let mut no_lines;
    let input: amp;mut dyn Iterator<Item = String> = match file {
        Ok(f) => {
            lines = io::BufReader::new(f).lines().map(Result::unwrap);
            amp;mut lines
        },
        Err(_) => {
            no_lines = iter::empty();
            amp;mut no_lines
        },
    };
    
    run_against_input(input);
}
 

Комментарии:

1. хм, какой трюк, не знал, что это действительно сработает, спасибо! : D

Ответ №2:

Вы можете использовать a Box вместо ссылки, чтобы значение было собственностью и не было удалено:

 let mut input: Box<dyn Iterator<Item = String>> = match file {
        Ok(f) => Box::new(io::BufReader::new(f).lines().map(|line| line.unwrap())),
        Err(_) => Box::new(Vec::new().into_iter()),
    };
 

игровая площадка

Если вы предпочитаете, вы также можете изменить свою функцию, чтобы использовать поле вместо ссылки:

 fn run_against_input(inp: Box<dyn Iterator<Item = String>>) {