代码之家  ›  专栏  ›  技术社区  ›  Scott Colby

如果没有位置参数,则使用STDIN

  •  1
  • Scott Colby  · 技术社区  · 5 年前

    我鼓掌 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 特质,a Box<Iterator<Item = String>> 是唯一的出路。是这样吗?

    0 回复  |  直到 5 年前
        1
  •  2
  •   Sven Marnach    5 年前

    很少有“唯一的路要走”,这种情况也不例外。另一种方法是使用静态分派而不是动态分派。

    主处理代码需要一个字符串迭代器作为输入。所以您可以定义这样的处理函数:

    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