[SOLVED] How to write Generics parameter for a Iterator born a special type item。


#1

like this:

fn kXor<T, Item> (p1: T, p2: T) -> bool
    where T: Iterator<Item>,
          Item: BitXor {

    for d in p1.iter().zip(p2.iter()).map(|(x, y)| x^y)

#2

There are a couple of problems in this code:

  • Item in the Iterator is not a type parameter but an associated type. You can imagine that the type parameter as an “input” to the type-level “function” and the associated type as an “output”, and thus you can simply use bounds like T: Iterator, T::Item: BitXor. (If you want to keep the Item type parameter for other reasons, T: Iterator<Item=Item>, Item: BitXor is also possible.)

  • Given the bounds, both p1 and p2 are already iterators and zip should be directly called against that. (i.e. p1.zip(p2).map(...)) If you want to receive, say, a normal Vec<Item> that can be converted to an iterator but is not an iterator itself, then T should have an IntoIterator bound instead; then you can use .into_iter() method to get a relevant iterator.


#4

Thank for you very detailed and useful answer.
below code could work well,although a little complex .

Is any other way build a Iterator without move ownership like .iter() instead of .into_iter()iter() impl in core::slice::SliceExt Unstable)

where T: IntoIterator + Copy,
          T::Item: BitXor,
          <<T as IntoIterator>::Item as BitXor>::Output: PartialEq + PartialOrd


#5

Do you have to use core? (If so, you probably want to use nightly anyway, as most tasks requiring core would need it anyway.) Otherwise a canonical iter method is very much stable.


#6

@lifthrasiir the trait SliceExt is unstable, though, so you can’t depend on it.

@sickHamm in most cases (including I think the cases you care about), if there’s a T::iter method, there’s also an impl of IntoIterator for &'a T. You can use this as a bound using higher ranked trait bounds, like this:

where for<'a> &'a T: IntoIterator,
      for<'a> <&'a T as IntoIterator>::Item: BitXor,
      for<'a> <<&'a T as IntoIterator>::Item as BitXor>::Output: PartialEq + PartialOrd

Your bounds become quite long unfortunately, but that tends to happen when your code is extremely abstract.

Someday there will probably be a trait like RefIterator, which defines the iter method, but to define that trait requires a feature that doesn’t exist in Rust yet.


#7

@withoutboats
It indeed could work; But I am unfamiliarity of where for<'a> &'a T: IntoIterator, Is about Deref coercions ?

https://doc.rust-lang.org/book/traits.html#where-clause
https://doc.rust-lang.org/reference.html#generic-functions


fn kXor<T> (iter:T)
    where for<'a> &'a T: IntoIterator {
    // where T: IntoIterator {       // couldn't work
    for i in iter.into_iter() {}; // why iter not move;
    for i in iter.into_iter() {};
}

fn main() {
    let v0 = vec![0u8; 20];
    kXor(v0);
}
   

#8

@sickHamm for<'a> is a higher-rank trait bound (HRTB). That’s an admittedly slightly obscure feature of the language. It’s not met very often and is not overly well documented. You can find it mentioned here in the Rustonomicon.


#9

It means that "for any lifetime 'a, &'a T implements IntoIterator", or even more colloquially, "any reference to T, of any lifetime, must implement IntoIterator". There’s an implementation of IntoIterator for &'a Vec<T> in the standard library, so you can pass a Vec<T> to kXor.

In general, implementations of IntoIterator for &'a T do the same thing that the T::iter method does.

There’s no deref coercion going on, more like ref coercion. It is implicitly referencing iter when you call into_iter, because method calls will insert reference operators as necessary, just as they will insert dereference operators. You could also write for i in &iter { } to be more clear that you’re only referencing iter, though.


#10

thank both of you, and sorry for late reply. @withoutboats I have spent some time to understand,

because method calls will insert reference operators as necessary,

I lookup the book and reference they both only say will autoref ( automatically borrow ) for foo(&self) when method-call as .foo() . But into_iter(self) take a self move ownership parameter, rust also autoref just because couldn’t determine is SELF has impl foo, but can determine &SELF impl foo.
Is this an undefined behave or magic ?