Как мне использовать STDIN, если никакие позиционные аргументы не даны с хлопком? - PullRequest
1 голос
/ 13 марта 2019

У меня есть хлопок App, как это:

let m = App::new("test")
    .arg(
        Arg::with_name("INPUT")
            .help("a string to be frobbed")
            .multiple(true),
    )
    .get_matches();

Я хочу читать аргументы как итерируемые строки, если есть какие-либо myapp str1 str2 str3, но если нет, действовать как фильтр и читать итерируемые строки из stdin cat afile | myapp. Это моя попытка:

let stdin = io::stdin();
let strings: Box<Iterator<Item = String>> = if m.is_present("INPUT") {
    Box::new(m.values_of("INPUT").unwrap().map(|ln| ln.to_string()))
} else {
    Box::new(stdin.lock().lines().map(|ln| ln.unwrap()))
};

for string in strings {
    frob(string)
}

Я считаю, что, поскольку мне просто нужна черта Iterator, Box<Iterator<Item = String>> - единственный путь. Это правильно?

1 Ответ

2 голосов
/ 13 марта 2019

Редко бывает "единственный путь", и этот случай ничем не отличается. Одним из альтернативных подходов было бы использование статической диспетчеризации вместо динамической диспетчеризации.

Ваш основной код обработки нуждается в итераторе строк в качестве входных данных. Таким образом, вы можете определить функцию обработки следующим образом:

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 для двух случаев. Какой из них выбрать, зависит от компромиссов с другими требованиями вашего кода, вашими требованиями к производительности и вашими личными предпочтениями.

...