在Rust中,打印迭代器并用空格分隔的惯用方式是什么?

41

我只想要一个由参数变量构成的用空格分隔的String,这些参数变量是通过std::env::args()获得的,我一直在使用fold函数来创建它,就像这样:

std::env::args()
    .fold("".to_string(), |accum, s| accum + &s + " ")

然而这会在末尾创建一个不必要的额外空格。我尝试使用 truncate 函数,但它不会返回一个 String,只是修改现有的 String,并且这需要创建一个中间绑定来使用 Stringlen() 调用来定义截断的 String 的长度(这本身也需要一个中间绑定,因为根据 Rust 当前的词法借用规则,参数需要被借用!)


1
@Ferruccio 这只会导致前导空格,我之所以这样做是因为尾随空格通常是不可见的,而前导空格可以在格式化中看到。不过我应该指定不需要前导和尾随空格。 - Harvey Adcock
3
如果你愿意添加依赖的话,itertools中有一个 intersperse 方法。我认为在 std 中没有一个简单的方法来做到这一点。请注意,我会尽力使翻译通俗易懂,但不改变原来的意思。 - Dogbert
1
是的,当然,我应该意识到那会发生。我以前遇到过这个问题(使用C#)。你需要检查accum是否为空。我不确定这是否是有效的Rust语法,但通常我会做类似这样的事情:accum +(如果accum ==“”{“”} else {“ ”})+&s - Ferruccio
2
@Ferruccio 是的,那样做可以,闭包看起来有点凌乱,但并不太糟糕。对于选择 itertools 方法的任何人,只需注意 - 在 itertools 中有一个 join 函数,它将所有元素连接成一个由指定分隔符分隔的单个字符串。 - Harvey Adcock
5
为什么经常在itertools中听到intersperse的说法?iterools实际上有用于格式化的方法,可以使用.join(" ").format_default(" ") - bluss
显示剩余3条评论
5个回答

29

如何在Rust中以惯用方式打印所有命令行参数?

fn main() {
    let mut args = std::env::args();

    if let Some(arg) = args.next() {
        print!("{}", arg);

        for arg in args {
            print!(" {}", arg);
        }
    }
}

或者更好的方法是使用Itertools的formatformat_with:

use itertools::Itertools; // 0.8.0

fn main() {
    println!("{}", std::env::args().format(" "));
}
我只想要一个用空格分隔的字符串。
fn args() -> String {
    let mut result = String::new();
    let mut args = std::env::args();

    if let Some(arg) = args.next() {
        result.push_str(&arg);

        for arg in args {
            result.push(' ');
            result.push_str(&arg);
        }
    }

    result
}

fn main() {
    println!("{}", args());
}

或者

fn args() -> String {
    let mut result = std::env::args().fold(String::new(), |s, arg| s + &arg + " ");
    result.pop();
    result
}

fn main() {
    println!("{}", args());
}
如果你使用Itertools,你可以在format! 宏中使用上面的 format/format_with 示例。
此外,join也是很有用的。
use itertools::Itertools; // 0.8.0

fn args() -> String {
    std::env::args().join(" ")
}

fn main() {
    println!("{}", args());
}

在其他情况下,您可能希望使用intersperse

use itertools::Itertools; // 0.8.0

fn args() -> String {
    std::env::args().intersperse(" ".to_string()).collect()
}

fn main() {
    println!("{}", args());
}

请注意,相比其他选择,这种方法效率不高,因为每次迭代都会克隆一个 String


3
如果你把for循环放在if let里面,就不需要使用fuse - malbarbo
3
使用 itertools,您只需要执行 std::env::args().join(" ")(感谢OP和@bluss在问题评论中提供的提示)。 - Dogbert
对于 intersperse 的例子,你难道不能直接调用 .collect::<String>() 而不是使用折叠吗? - Addison
@Addison 是的!我猜我从早期带有 fold 的例子中复制粘贴,没有进行关键思考。 - Shepmaster

5
这是一个使用迭代器的解决方案,且不依赖任何非标准库(性能表现优异,需要在启用优化的发布模式下运行[playground])。
use std::iter::once;

fn join_iter<T>(
    mut iter: impl Iterator<Item = T>,
    sep: impl Fn(&T) -> T,
) -> impl Iterator<Item = T> {
    iter.next()
        .into_iter()
        .chain(iter.flat_map(move |s| once(sep(&s)).chain(once(s))))
}

fn examples() {
    let iter = [1, 3, 5, 7, 9].iter().cloned();
    println!("{:?}", join_iter(iter, |v| v - 1).collect::<Vec<_>>());
    // [1, 2, 3, 4, 5, 6, 7, 8, 9]

    let iter = ["Hello", "World"].iter().cloned();
    let sep = ", ";
    println!("{:?}", join_iter(iter, |_| sep).collect::<String>());
    // "Hello, World"
}

fn args() -> String {
    join_iter(std::env::args(), |_| " ".to_string()).collect()
}

fn main() {
    examples();

    println!("{}", args());
}

3

这里提供一种方法,用于从std::env::args()获取参数变量并返回一个以空格分隔的String

fn args_string() -> String {
    let mut out = String::new();
    let mut sep = "";
    for arg in std::env::args() {
        out.push_str(sep);
        out.push_str(&*arg);
        sep = " ";
    }
    out
}

pub fn main() {
    println!("{}", args_string());
}

2
这里是一个使用fold的版本。
fn join<I, T, D>(iter: I, separator: D) -> String
where
    T: Display,
    D: Display,
    I: Iterator<Item = T>,
{
    match iter.fold(None, |a:Option<String>, b| {
        Some(match a {
            None => format!("{}", &b),
            Some(mut a) => {
                write!(a, "{}{}", separator, b).unwrap();
                a
            }
        })
    }) {
        None => String::new(),
        Some(rval) => rval,
    }
}

0
fn main() {
    println!("{}", std::env::args().collect::<Vec<_>>().join(" "));
}

请记住,Stack Overflow 不仅仅是为了解决眼前的问题,还要帮助未来的读者找到类似问题的解决方案,这需要理解底层代码。这对于我们社区中的初学者来说尤为重要,因为他们可能对语法不太熟悉。鉴于此,你能否[编辑]你的回答,包括对你所做的事情的解释以及为什么你认为这是最佳方法? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接