I am trying to do something simple. In a slice of u8
, I want to find occurrence of two characters "\r\n"
. However, I cannot convert that slice into String
using from_utf8
because parts of slice after "\r\n"
may not be utf-8 and as far as possible I don't want to use from_utf8_unchecked
. So I tried something like following.
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
}
And I get following compilation errors, understandably. However I am not too sure how to go about doing this. If it were a str
, it was simply .find("\r\n")
.
Compilation Error ->
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
May be I am missing something really simple, but stuck on this for quite some time now.
Usually, the best way to write this sort of code is to not use Peekable
at all. It's a tricky API to use because you often want to call peek
when you are in the middle of iterating, which usually means you have already borrowed the iterator mutably so you can't borrow it again.
But, since you asked about Peekable
specifically, you could rewrite your code to explicitly call next
in a loop, which is often the only way to use 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
}
Generally, a better method for look-ahead is to use slice::windows
or tuple_windows
from itertools
.
Given that your input is a slice, you can use 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
}
In general though, I prefer the syntax of the itertools
method, because you can pattern match on the tuple which feels cleaner than indexing into a slice:
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
}
Or, even better:
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')
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With