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

gab*_*jit 5 iterator peek rust regex-lookarounds

我正在尝试做一些简单的事情。在切片中u8,我想找到两个字符的出现"\r\n"。但是,我无法将该切片转换为Stringusingfrom_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
}
Run Code Online (Sandbox Code Playgroud)

我得到以下编译错误,这是可以理解的。但是,我不太确定如何去做。如果是str,那就是.find("\r\n")

编译错误->

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
}
Run Code Online (Sandbox Code Playgroud)

可能是我错过了一些非常简单的东西,但现在已经坚持了很长一段时间。

Pet*_*all 11

通常,编写此类代码的最佳方法是根本不使用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
}
Run Code Online (Sandbox Code Playgroud)

通常,更好的前瞻方法是使用slice::windowstuple_windowsfrom itertools

鉴于您的输入是一个切片,您可以使用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
}
Run Code Online (Sandbox Code Playgroud)

不过,总的来说,我更喜欢该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
}
Run Code Online (Sandbox Code Playgroud)

或者,甚至更好:

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')
}
Run Code Online (Sandbox Code Playgroud)