在Rust中,如何在容器中的每个值之间打印某些内容是最佳方式?

12
我想打印向量中每个项目,并用逗号分隔。您可以使用数字索引:
for i in 0..vec.len() {
    print!("{}", vec[i]);
    if i < vec.len() - 1 {
        print!(", ");
    }
}

但是,如果你只有一个Iterator怎么办?你要么需要特殊处理第一个或最后一个值,要么创建一个自定义的迭代器,这似乎需要很多工作。

在Rust中,有没有更清晰和惯用的表达方式呢?


请记住,任何使用range(0, vec.len())的代码都可能不是正确的方法;迭代是首选的操作方式,并且具有更高的效率(可以跳过边界检查)。 - Chris Morgan
4个回答

12

如果您想避免使用变量来检查元素是否是第一个,您可以利用迭代器的.take().skip()方法:

for e in vec.iter().take(1) {
    print!("{}", e);
}
for e in vec.iter().skip(1) {
    print!(", {}", e);
}

或将所有内容紧凑折叠:

vec.iter().fold(true, |first, elem| {
    if !first { print(", "); }
    print(elem);
    false
});

1
使用类似 fold 的东西的缺点是它会占用控制流,因此您无法执行早期中断、返回等操作。当然,在这种简单情况下,这是可以接受的。 - Chris Morgan

8
您可以对第一个元素进行特殊处理,然后将所有后续元素视为相同处理:
let mut iter = vec.iter();
if let Some(item) = iter.next() {
    print!("{}", item);

    for item in iter {
        print!("<separator>{}", item);        
    }
}

如果您使用Itertools::format,那么这将更加容易:
println!("{}", vec.iter().format("<separator>"));

4
let first = true;
for item in iterator {
    if !first {
        print(", ");
    }
    print(item);
    first = false;
}

没错,那个可行。我猜迭代器上没有is_first方法?(哦,嘿,你也在墨尔本!是什么让你接触Rust? :) - Michael Day
迭代器非常轻量级;因此不可能合理地存在位置的概念。可以在迭代器周围包装一个允许包含这个概念的包装器,或者如果您正在使用 iterator.enumerate(),则可以检查元组中的第一个值是否为0,但是不能有一种通用的方法适用于所有迭代器。 - Chris Morgan
为什么这个答案比@shepmaster的答案(https://dev59.com/hl8d5IYBdhLWcg3wlzH6#45134036)更“惯用”? (指Itertools::format答案)这是因为自2014年以来经过了几年吗? - x10an14
1
@x10an14:对于这样的事情,个人而言,我不愿意为了这么小的东西引入新的依赖项。它们会增加。如果我已经在使用itertools,我会考虑将其用于此目的;但我的方法或Shepmaster的展开第一次迭代方法会更快(编译和运行)并且更轻(itertools的格式使用Rc分配)。 - Chris Morgan
啊,好的。我没有意识到itertools是一个非标准库依赖项。+1! - x10an14
显示剩余3条评论

3

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