【问题标题】:usize ownership issue with iterators使用迭代器的所有权问题
【发布时间】:2021-08-14 05:10:07
【问题描述】:

以下代码采用[[char; 10]; 3] 键盘布局并返回按键所需的手指和手指移动。

根据我对 Rust 中所有权和借用的初步理解,我认为开发人员只需要担心堆上分配的内存的所有权。另一方面,基元会在必要时复制自己。

impl KeyboardBuilder {
    fn build(char_layout: [[char; 10]; 3]) -> HashMap<char, Key> {
        char_layout
            .iter()
            .enumerate()
            .map(|(y, row)| {
                row.iter().enumerate().map(|(x, char)| {
                    (char, Key::new(
                        char,
                        KeyboardBuilder::get_finger(x as u8),
                        KeyboardBuilder::get_pos(x as u8, y as u8),
                    ))
                })
            })
            .flatten()
            .collect()
    }
}

要更正代码,我必须更改char 的所有权,并使用move 移动所有权。

char_layout
    .iter()
    .enumerate()
    .map(|(y, row)| {
        row.iter().enumerate().map(move |(x, char)| {
        //                         ^^^^
            (char.to_owned(), Key::new(
                // ^^^^^^^^^
                char.to_owned(),
                //   ^^^^^^^^^
                KeyboardBuilder::get_finger(x as u8),
                KeyboardBuilder::get_pos(x as u8, y as u8),
            ))
        })
    })

我有多个问题,

  • 为什么我必须更改原语的所有权?为什么他们不创建从迭代器到迭代器的副本?
  • moveto_owned() 的功能和区别是什么?
  • 我必须在tupalKey::new 参数中都使用to_owned()。现在char 的所有者是谁?

【问题讨论】:

    标签: rust


    【解决方案1】:

    为什么我必须更改原语的所有权?为什么他们不创建从迭代器到迭代器的副本?

    这不是所有权问题,真的;这只是类型不匹配。你没有原始人;你有一个原语的reference&amp;char 而不是char。发生这种情况是因为每当您使用 .iter() 迭代集合时,您都会获得对集合项的引用——即使它们是更适合按值传递的简单原语。

    对此有几个修复;你可以写*来取消引用:

        fn build(char_layout: [[char; 10]; 3]) -> HashMap<char, Key> {
            char_layout
                .iter()
                .enumerate()
                .map(|(y, row)| {
                    row.iter().enumerate().map(|(x, char)| {
                        (*char, Key::new(...))
                    })
                })
                .flatten()
                .collect()
        }
    

    您可以对引用进行模式匹配:

        fn build(char_layout: [[char; 10]; 3]) -> HashMap<char, Key> {
            char_layout
                .iter()
                .enumerate()
                .map(|(y, row)| {
                    row.iter().enumerate().map(|(x, &char)| {
                        (char, Key::new(...))
                    })
                })
                .flatten()
                .collect()
        }
    

    您可以使用Iterator::copied,它将引用的迭代器转换为其所指对象的副本的迭代器:

        fn build(char_layout: [[char; 10]; 3]) -> HashMap<char, Key> {
            char_layout
                .iter()
                .enumerate()
                .map(|(y, row)| {
                    row.iter().copied().enumerate().map(|(x, char)| {
                        (char, Key::new(...))
                    })
                })
                .flatten()
                .collect()
        }
    

    或者,您可以按照您的发现使用.to_owned()。所有这些都会复制引用背后的值。另一种选择是按值遍历数组(消耗数组);不幸的是,由于一些向后兼容性问题,这很笨拙,但希望在未来的 Rust 版本中更方便。

    use std::array::IntoIter;
    ...
        fn build(char_layout: [[char; 10]; 3]) -> HashMap<char, Key> {
            IntoIter::new(char_layout)
                .enumerate()
                .map(|(y, row)| {
                    IntoIter::new(row).enumerate().map(|(x, char)| {
                        (char, Key::new(...))
                    })
                })
                .flatten()
                .collect()
        }
    

    move.to_owned() 的功能和区别是什么?

    move 在这里是不必要的(但无害的)。我怀疑您添加它是为了在您进行实验时响应一个不相关的错误。一般来说,move 改变了闭包(传递给.map() 的匿名函数)从其环境中捕获变量的方式,以移动(或复制)它们而不是引用它们。对于您的情况,这不是必需的,因为您没有返回一个闭包或做任何其他需要闭包才能比创建它的函数寿命更长的事情。

    .to_owned() 引用一个值并构造该值的“拥有”副本;对于原语,它与取消引用引用相同。它最常用于 &amp;str 引用,其中拥有的值是不同的类型 (String)。

    我必须在 tupal 和 Key::new 参数中都使用 .to_owned()。现在谁是 char 的所有者?

    元组和Key 拥有角色的那些副本.to_owned()取得所有权;它会复制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-18
      • 1970-01-01
      • 2011-08-06
      • 2016-03-16
      • 2010-12-23
      • 2011-02-13
      相关资源
      最近更新 更多