Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: Instead, this happened: <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: The reason is that the iterator calculates its size hint in terms of the contained iterator size hint, assuming that each character can correspond to either 1 or 2 code units. In the case that the iterator is NOT in the middle of a surrogate pair, this leads to too-low lower bounds and too high upper-bounds. In the case that the iterator IS in the middle of a surrogate pair, the remaining code unit is not taken into account as the iterator has advanced past this point. The actual calculation should be done in terms of the remaining bytes: The lower bound is achieved by assuming the remaining bytes consist of as many 3-byte sequences as possible, optionally followed by a 1 or 2-byte sequence, leading to a lower bound of The upper bound is achieved by assuming the remaining bytes consist of 1-byte sequences, leading to an upper bound of . In the case of the iterator being positioned in the middle of a surrogate pair, both these values should be increased by 1.
, size hints are allowed to be looser than necessary. As the documentation says But if it can be made tighter while still being correct for all values that's fine. Edit: Oh, yeah. This one is actually a bug since the iterator is not exhausted at that point.

Discussion:
} #[test] <ins> fn test_utf16_size_hint() { assert_eq!("".encode_utf16().size_hint(), (0, Some(0))); assert_eq!("123".encode_utf16().size_hint(), (1, Some(3))); assert_eq!("1234".encode_utf16().size_hint(), (2, Some(4))); assert_eq!("12345678".encode_utf16().size_hint(), (3, Some(8))); fn hint_vec(src: &str) -> Vec<(usize, Option<usize>)> { let mut it = src.encode_utf16(); let mut result = Vec::new(); result.push(it.size_hint()); while it.next().is_some() { result.push(it.size_hint()) } result } assert_eq!(hint_vec("12"), [(1, Some(2)), (1, Some(1)), (0, Some(0))]); assert_eq!(hint_vec("u{101234}"), [(2, Some(4)), (1, Some(1)), (0, Some(0))]); assert_eq!(hint_vec("u{101234}a"), [(2, Some(5)), (2, Some(2)), (1, Some(1)), (0, Some(0))]); } #[test] </ins> fn starts_with_in_unicode() { assert!(!"├── Cargo.toml".starts_with("# ")); }

#[inline] fn size_hint(&self) -> (usize, Option<usize>) { <del> let (low, high) = self.chars.size_hint(); // every char gets either one u16 or two u16, // so this iterator is between 1 or 2 times as // long as the underlying iterator. (low, high.and_then(|n| n.checked_mul(2))) </del> <ins> let len = self.chars.iter.len(); // The highest bytes:code units ratio occurs for 3-byte sequences, // since a 4-byte sequence results in 2 code units. The lower bound // is therefore determined by assuming the remaining bytes contain as // many 3-byte sequences as possible. The highest bytes:code units // ratio is for 1-byte sequences, so use this for the upper bound. // `(len + 2)` can't overflow, because we know that the `slice::Iter` // belongs to a slice in memory which has a maximum length of // `isize::MAX` (that's well below `usize::MAX`) if self.extra == 0 { ((len + 2) / 3, Some(len)) } else { // We're in the middle of a surrogate pair, so add the remaining // surrogate to the bounds. ((len + 2) / 3 + 1, Some(len + 1)) } </ins> } }