如何在Rust中正确使用`peek()`?

13

我正在尝试做一些简单的事情,在一个切片中,我想查找两个字符"\r\n"的出现次数。然而,我不能使用from_utf8将该切片转换为字符串,因为"\r\n"之后的部分可能不是utf-8编码,而且我尽量不想使用from_utf8_unchecked。所以我尝试了以下方法。

fn find_crlf(text: &[u8]) -> Option<usize> {
    let mut textiter = text.iter().peekable();

    for (idx, &elem) in textiter.enumerate() {
        if Some(&elem) == Some(&b'\r') {
            if textiter.peek() == Some(&&b'\n') {
                return Some(idx);
            }
        }
    }
    None
}

我遇到了以下编译错误,这是可以理解的。但是我不太确定该怎么做。如果它是一个 str,那么只需使用 .find("\r\n")

编译错误 ->

error[E0382]: borrow of moved value: `textiter`
 --> src/lib.rs:6:16
  |
2 |     let mut textiter = text.iter().peekable();
  |         ------------ move occurs because `textiter` has type `std::iter::Peekable<std::slice::Iter<'_, u8>>`, which does not implement the `Copy` trait
3 | 
4 |     for (idx, &elem) in textiter.enumerate() {
  |                         -------- value moved here
5 |         if Some(&elem) == Some(&b'\r') {
6 |             if textiter.peek() == Some(&&b'\n') {
  |                ^^^^^^^^ value borrowed here after move

也许我错过了一些非常简单的东西,但是我已经卡在这里很长时间了。


1
你可能会对这个问题感兴趣:如何在一个 u8 切片中查找子序列。链接 - SCappella
1个回答

27

通常,编写这种代码的最佳方式是根本不使用 Peekable。这是一个棘手的 API,因为你经常想要在迭代过程中调用 peek,这通常意味着你已经可变地借用了迭代器,所以你不能再次借用它。

但是,既然你特别问到了 Peekable,你可以重写你的代码,在循环中显式地调用 next,这通常是使用 peek 的唯一方式:

fn find_crlf(text: &[u8]) -> Option<usize> {
    let mut textiter = text.iter().enumerate().peekable();
    while let Some((idx, &elem)) = textiter.next() {
        if Some(&elem) == Some(&b'\r') {
            if let Some((_, &b'\n')) = textiter.peek() {
                return Some(idx);
            }
        }
    }
    None
}

一般来说,预见性更好的方法是使用slice::windowsitertools中的tuple_windows

鉴于您的输入是一个切片,您可以使用slice::windows

fn find_crlf(text: &[u8]) -> Option<usize> {
    for (idx, window) in text.windows(2).enumerate() {
        if window[0] == b'\r' && window[1] == b'\n' {
            return Some(idx);
        }
    }
    None
}

总的来说,我更喜欢使用itertools方法的语法,因为你可以在元组上进行模式匹配,这比索引切片更加清晰:

use itertools::Itertools; // 0.9.0

fn find_crlf(text: &[u8]) -> Option<usize> {
    for (idx, (&elem, &next)) in text.iter().tuple_windows().enumerate() {
        if elem == b'\r' && next == b'\n' {
            return Some(idx);
        }
    }
    None
}

或者,更好的选择:

use itertools::Itertools; // 0.9.0

fn find_crlf(text: &[u8]) -> Option<usize> {
    text.iter()
        .tuple_windows()
        .position(|(elem, next)| elem == &b'\r' && next == &b'\n')
}

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