#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>>) {