Pb with auto-impl trait for struct when &struct impl IntoIterator

Hello,
I tried to define a generic implementation of a trait for all type than implement a trait + IntoIterator is implemented for ref on the type.
After quite a bit of research, I finally found the syntax to express this in where close, by when I tried it's don't work. The compiler say :

error[E0599]: the method print_values exists for struct RangeWithInc, but its trait bounds were not satisfied

But if I manually implement the trait for the type, the compiler say :

warning: conflicting implementations of trait ListValues for type RangeWithInc

Someone can tell me if I have made an error in the ListValues generic implementation definition, or if it's should work and I need to create a bug ?
Thank you

Example code :

trait Range {
    fn name(&self) -> &str;
    fn start(&self) -> u32;
    fn end(&self) -> u32;
}
trait ListValues {
    fn print_values(&self) -> String;
}
struct RangeWithInc {
    end: u32,
    inc: u32,
}
impl RangeWithInc {
    pub fn inc(&self) -> u32 {
        self.inc
    }
}
impl Range for RangeWithInc {
    fn name(&self) -> &str {
        "demo"
    }
    fn start(&self) -> u32 {
        0
    }
    fn end(&self) -> u32 {
        self.end
    }
}
impl<'a> IntoIterator for &'a RangeWithInc {
    type Item = u32;
    type IntoIter = DataIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        DataIterator {
            range: self,
            progress: 0,
        }
    }
}
struct DataIterator<'a> {
    range: &'a RangeWithInc,
    progress: u32,
}

impl Iterator for DataIterator<'_> {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        self.progress += self.range.inc();
        if self.progress < self.range.end() {
            Some(self.range.start() + self.progress)
        } else {
            None
        }
    }
}

// generic impl of `ListValues`
impl<I, U> ListValues for U
where
    I: Iterator<Item = u32>,
    for<'a> &'a U: IntoIterator<Item = u32, IntoIter = I>,
    U: Range,
{
    fn print_values(&self) -> String {
        let init = String::from(self.name());
        //let ref_self: &U = self.into::<R>();
        let iter = self.into_iter();
        iter.fold(init, |val, cur| format!("{val}, {cur}"))
    }
}

// // dedicated impl of `ListValues`
// impl ListValues for RangeWithInc {
//     fn print_values(&self) -> String {
//         let init = String::from(self.name());
//         //let ref_self: &U = self.into::<R>();
//         let iter = self.into_iter();
//         iter.fold(init, |val, cur| format!("{val}, {cur}"))
//     }
// }

fn main() {
    let range = RangeWithInc { end: 25, inc: 3 };
    let values = range.print_values();
    println!("{values}");
}

It looks like explicitly referring to I in the generic implementation prevents it from accessing the lifetime parameter 'a (which it needs because DataIterator takes a lifetime parameter). You can just remove I altogether since IntoIterator already includes the output of into_iter() being an Iterator:

impl<U> ListValues for U
where
    for<'a> &'a U: IntoIterator<Item = u32>,
    U: Range,
{
    fn print_values(&self) -> String {
        // ...
    }
}
1 Like

Oh! I was looking to specify more things to help/force, or have better error. :upside_down_face:

It's work also in my full case.
Thank you

Phrased differently, type parameters like I must resolve to a single type, and types that differ by lifetime are distinct types.

Phrased differently, Rust has no "generic type constructors" where you could state something like

// Made up syntax/functionality
impl<I<'*>, U> ListValues for U
where
    for<'a> I<'a>: Iterator<Item = u32>,
    for<'a> &'a U: IntoIterator<Item = u32, IntoIter = I<'a>>,
    U: Range,

(Though you can sometimes emulate it with traits.)

Fortunately as you pointed out, that's not actually necessary in this case, since you can just not equate the associated type with a generic type parameter.

2 Likes

Effectively, I already read this, but I have difficulty to integrate in my reflection when I am faced with a problem.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.