Редко бывает "единственный путь", и этот случай ничем не отличается. Одним из альтернативных подходов было бы использование статической диспетчеризации вместо динамической диспетчеризации.
Ваш основной код обработки нуждается в итераторе строк в качестве входных данных. Таким образом, вы можете определить функцию обработки следующим образом:
fn process<I: IntoIterator<Item = String>>(strings: I) {
for string in strings {
frob(string);
}
}
Вызов этого кода может выглядеть следующим образом:
match m.values_of("INPUT") {
Some(values) => process(values.map(|ln| ln.to_string())),
None => process(io::stdin().lock().lines().map(|ln| ln.unwrap())),
}
Компилятор выдаст две разные версии process()
, по одной для каждого типа итератора. Каждая версия статически вызывает функции итератора, для которых она скомпилирована, и в операторе match
имеется только одна отправка нужной функции.
(Возможно, я здесь неправильно понял некоторые детали, но вы поняли идею.)
Ваша версия, с другой стороны, использует тип Box<dyn Iterator<Item = String>>
, поэтому итераторы будут размещаться в куче, и при каждом вызове next()
для итератора будет динамическая диспетчеризация. Что, вероятно, хорошо.
Существует, безусловно, больше способов структурирования кода и распределения между двумя различными видами ввода, например, используя тип Either
из ящика either
или просто записав два разных цикла for
для двух случаев. Какой из них выбрать, зависит от компромиссов с другими требованиями вашего кода, вашими требованиями к производительности и вашими личными предпочтениями.