如何在 Rust 中正确使用 `peek()`?
How to correctly use `peek()` in Rust?
我正在尝试做一些简单的事情。在 u8
的一片中,我想找到两个字符 "\r\n"
的出现。但是,我无法使用 from_utf8
将该切片转换为 String
,因为 "\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
可能是我遗漏了一些非常简单的东西,但现在坚持了很长一段时间。
通常,编写此类代码的最佳方式是完全不使用 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
}
通常,更好的前瞻方法是使用 itertools
中的 slice::windows
或 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')
}
我正在尝试做一些简单的事情。在 u8
的一片中,我想找到两个字符 "\r\n"
的出现。但是,我无法使用 from_utf8
将该切片转换为 String
,因为 "\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
可能是我遗漏了一些非常简单的东西,但现在坚持了很长一段时间。
通常,编写此类代码的最佳方式是完全不使用 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
}
通常,更好的前瞻方法是使用 itertools
中的 slice::windows
或 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')
}