代码之家  ›  专栏  ›  技术社区  ›  Harry

按相反顺序克隆集合

  •  -1
  • Harry  · 技术社区  · 8 月前

    如何以相反的方式克隆集合。我试着用 DoubleEndedIterator 向后迭代以克隆集合的每个元素。

    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[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
    
    2 回复  |  直到 8 月前
        1
  •  1
  •   Finomnis    8 月前

    在撰写本文时,特质解决者似乎无法解决这种间接问题。

    似乎需要手动注释类型,如下所示:

    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);
    }
    

    (注意,我删除了一些不必要的注释)

    你可以进一步优化这一步,这样你只需要注释返回类型,因为(通过一点注释的痛苦)向量的元素可以由编译器确定:

    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);
    }
    

    但我认为,在撰写本文时,这已经是最好的了。

        2
  •  0
  •   Finomnis    8 月前

    就我个人而言,我并不完全了解你的确切用例(这个问题在任何特定用例中都很模糊),但只要在需要的地方使用这4行代码,这个问题就可以很容易地得到解决,就我个人来说,如果把它拉到一个专用的fn中,我会比任何事情都更困惑。这是一种常见且易于识别的模式的变体, .iter().{cloned/map/filter/flatten}.collect() .

    正如错误消息所示,该问题源于编译器中的临时限制,希望很快能得到修补。编译器无法处理泛型,特别是当涉及引用时,IntoIterator::IntoIter上的trait边界。

    其他选择是取消fn的通用性;让它采用特定的数据类型,从而绕过限制(特别是考虑是否需要将此fn作为回调传递),或者你可以让fn用户直接创建并提供DEI本身,基本上传递 clone_backward(first_list.iter().rev()) 尽管这将进一步使函数变得可读性较差、不可链接,否则现在是2行的,非常简单。

    推荐文章