What it does
Iterators are often created that iterate over some contained inner collection, or have a size that is known based on a range. I ran into this when building iterators for FFI types that use the struct hack, but this also occurs elsewhere, e.g. the proc_macro::token_stream::IntoIter type is a wrapper around https://doc.rust-lang.org/std/vec/struct.IntoIter.html, but while the latter is marked as ExactSizeIterator, the wrapper isn't
Advantage
Those unfamiliar with ExactSizeIterator or who forgot to add it are prompted to add an implementation, and get the benefits of that trait
Drawbacks
If the ExactSizeIterator isn't going to be useful this is just another warning/suggestion
Example
From https://github.com/rust-lang/rust/blob/3664b37017c529cad0f0ed259769f15743c21a6a/library/proc_macro/src/lib.rs#L532
#[derive(Clone)]
pub struct IntoIter(
std::vec::IntoIter<
bridge::TokenTree<
bridge::client::TokenStream,
bridge::client::Span,
bridge::client::Symbol,
>,
>,
);
impl Iterator for IntoIter {
type Item = TokenTree;
fn next(&mut self) -> Option<TokenTree> {
// Elided, not applicable to this suggestion
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize {
self.0.count()
}
}
Could be written as:
// All of the code above, and also
impl ExactSizeIterator for IntoIter {}
Comparison with existing lints
clippy::should_implement_trait suggests existing methods that should be moved to trait implementations, because the code has already been written - here, the suggested addition of the ExactSizeIterator doesn't require moving any code around, just adding another line
Additional Context
Simplified version of the code that motivated this:
#![allow(non_camel_case_types)]
use std::ops::Range;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct zend_attribute_arg {
// Stub for demo
value: u32,
}
#[repr(C)]
struct zend_attribute {
argc: u32,
args: [zend_attribute_arg; 1],
}
impl zend_attribute {
fn args_iter(&self) -> ZendAttributeArgIter<'_> {
ZendAttributeArgIter {
raw: self,
range: 0..(self.argc as usize),
}
}
}
struct ZendAttributeArgIter<'a> {
raw: &'a zend_attribute,
range: Range<usize>,
}
impl Iterator for ZendAttributeArgIter<'_> {
type Item = zend_attribute_arg;
fn next(&mut self) -> Option<Self::Item> {
self.range
.next()
.map(|i| unsafe { *self.raw.args.as_ptr().add(i) })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
fn main() {
let args = [
zend_attribute_arg { value: 5 },
zend_attribute_arg { value: 10 },
zend_attribute_arg { value: 1 },
];
let attrib = zend_attribute {
argc: args.len().try_into().unwrap(),
args: unsafe { *args.as_ptr().cast() },
};
for arg in attrib.args_iter() {
println!("{arg:?}");
}
}
What it does
Iterators are often created that iterate over some contained inner collection, or have a size that is known based on a range. I ran into this when building iterators for FFI types that use the struct hack, but this also occurs elsewhere, e.g. the
proc_macro::token_stream::IntoItertype is a wrapper around https://doc.rust-lang.org/std/vec/struct.IntoIter.html, but while the latter is marked asExactSizeIterator, the wrapper isn'tAdvantage
Those unfamiliar with ExactSizeIterator or who forgot to add it are prompted to add an implementation, and get the benefits of that trait
Drawbacks
If the ExactSizeIterator isn't going to be useful this is just another warning/suggestion
Example
From https://github.com/rust-lang/rust/blob/3664b37017c529cad0f0ed259769f15743c21a6a/library/proc_macro/src/lib.rs#L532
Could be written as:
Comparison with existing lints
clippy::should_implement_traitsuggests existing methods that should be moved to trait implementations, because the code has already been written - here, the suggested addition of theExactSizeIteratordoesn't require moving any code around, just adding another lineAdditional Context
Simplified version of the code that motivated this: