rustiterator

Clone a collection in reverse order


How to clone a collection in reverse manner. I tried using DoubleEndedIterator to iterate backwards to clone each element of the collection.

fn clone_backward<List, ItemType>(list: &List) -> List
where
    for<'a> &'a List: IntoIterator<Item = &'a ItemType>,
    for<'a> <&'a List as IntoIterator>::IntoIter: DoubleEndedIterator,
    for<'a> &'a List::IntoIter: DoubleEndedIterator<Item = &'a ItemType>,
    List: FromIterator<ItemType> + IntoIterator,
    ItemType: Clone,
{
    list.into_iter()
    .rev()
    .cloned()
    .collect()
}

fn main() {
    let first_list = vec![1, 2, 3, 4, 5];
    let second_list: Vec<i32> = clone_backward(&first_list);
        
    assert_eq!(vec![5, 4, 3, 2, 1], second_list);
}

Error

error[E0277]: the trait bound `for<'a> <&'a _ as IntoIterator>::IntoIter: DoubleEndedIterator` is not satisfied
  --> src/main.rs:17:48
   |
17 |     let second_list: Vec<i32> = clone_backward(&first_list);
   |                                 -------------- ^^^^^^^^^^^ the trait `for<'a> DoubleEndedIterator` is not implemented for `<&'a _ as IntoIterator>::IntoIter`
   |                                 |
   |                                 required by a bound introduced by this call
   |
note: this is a known limitation of the trait solver that will be lifted in the future
  --> src/main.rs:17:48
   |
17 |     let second_list: Vec<i32> = clone_backward(&first_list);
   |                                 ---------------^^^^^^^^^^^-
   |                                 |              |
   |                                 |              the trait solver is unable to infer the generic types that should be inferred from this argument
   |                                 add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required by a bound in `clone_backward`
  --> src/main.rs:4:51
   |
1  | fn clone_backward<List, ItemType>(list: &List) -> List
   |    -------------- required by a bound in this function
...
4  |     for<'a> <&'a List as IntoIterator>::IntoIter: DoubleEndedIterator,
   |                                                   ^^^^^^^^^^^^^^^^^^^ required by this bound in `clone_backward`

error[E0277]: the trait bound `for<'a> &'a _: DoubleEndedIterator` is not satisfied
  --> src/main.rs:17:48
   |
17 |     let second_list: Vec<i32> = clone_backward(&first_list);
   |                                 -------------- ^^^^^^^^^^^ the trait `for<'a> DoubleEndedIterator` is not implemented for `&'a _`
   |                                 |
   |                                 required by a bound introduced by this call
   |
note: required by a bound in `clone_backward`
  --> src/main.rs:5:33
   |
1  | fn clone_backward<List, ItemType>(list: &List) -> List
   |    -------------- required by a bound in this function
...
5  |     for<'a> &'a List::IntoIter: DoubleEndedIterator<Item = &'a ItemType>,
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `clone_backward`
help: consider removing the leading `&`-reference
   |
17 -     let second_list: Vec<i32> = clone_backward(&first_list);
17 +     let second_list: Vec<i32> = clone_backward(first_list);
   |

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to 2 previous errors

Solution

  • It seems that at the time of writing, the trait solver is unable to resolve this kind if indirection.

    It seems that it is required to annotate the types manually, like so:

    fn clone_backward<List, ItemType>(list: &List) -> List
    where
        for<'a> &'a List: IntoIterator<Item = &'a ItemType>,
        for<'a> <&'a List as IntoIterator>::IntoIter: DoubleEndedIterator,
        List: FromIterator<ItemType>,
        ItemType: Clone,
    {
        list.into_iter().rev().cloned().collect()
    }
    
    fn main() {
        let first_list = vec![1, 2, 3, 4, 5];
        let second_list: Vec<i32> = clone_backward::<Vec<i32>, _>(&first_list);
    
        assert_eq!(vec![5, 4, 3, 2, 1], second_list);
    }
    

    (Note there were a couple of unnecessary annotations I removed)

    You can optimize this one step further so you only need to annotate the return type, as (through a little bit of annotation pain) the element of the vector can be determined by the compiler:

    fn clone_backward<List>(list: &List) -> List
    where
        for<'a> &'a List: IntoIterator,
        for<'a> <&'a List as IntoIterator>::IntoIter: DoubleEndedIterator,
        for<'a> <&'a List as IntoIterator>::Item: std::ops::Deref,
        for<'a> <<&'a List as IntoIterator>::Item as std::ops::Deref>::Target: Clone,
        for<'a> List: FromIterator<<<&'a List as IntoIterator>::Item as std::ops::Deref>::Target>,
    {
        list.into_iter().rev().map(|val| (*val).clone()).collect()
    }
    
    fn main() {
        let first_list = vec![1, 2, 3, 4, 5];
        let second_list: Vec<i32> = clone_backward::<Vec<i32>>(&first_list);
    
        assert_eq!(vec![5, 4, 3, 2, 1], second_list);
    }
    

    But I think, at the time of writing, this is as good as it will get.