正如您所指出的,每个take_while
调用都重复了iter
,因为take_while
取self
,Peekable
个字符的迭代器是Copy
.(Only true before Rust 1.0 — editor)
您希望每次都修改迭代器,也就是说,take_while
对迭代器的&mut
进行操作.这正是.by_ref
适配器的用途:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
match iter.peek().map(|c| *c) {
None => return bits,
Some(c) => if c.is_digit(10) {
bits.push(iter.by_ref().take_while(|c| c.is_digit(10)).collect());
} else {
bits.push(iter.by_ref().take_while(|c| !c.is_digit(10)).collect());
},
}
}
}
fn main() {
println!("{:?}", split("123abc456def"))
}
输出
["123", "bc", "56", "ef"]
然而,我认为这是不对的.
实际上,我建议使用char_indices
迭代器将其作为普通的for
循环编写:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(input[start..i].to_string());
is_digit = this_is_digit;
start = i;
}
}
bits.push(input[start..].to_string());
bits
}
这种形式还允许用更少的分配来实现这一点(也就是说,不需要String
),因 for each 返回值只是input
的一部分,我们可以使用生命周期来说明:
pub fn split<'a>(input: &'a str) -> Vec<&'a str> {
let mut bits = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(&input[start..i]);
is_digit = this_is_digit;
start = i;
}
}
bits.push(&input[start..]);
bits
}
改变的只是类型签名,删除了Vec<String>
类型提示和.to_string
个调用.
人们甚至可以编写这样的迭代器,以避免分配Vec
.比如fn split<'a>(input: &'a str) -> Splits<'a> { /* construct a Splits */ }
,其中Splits
是一个实现Iterator<&'a str>
的 struct .