微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用迭代器的所有权问题

如何解决使用迭代器的所有权问题

以下代码采用 [[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),))
        })
    })

我有多个问题,

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

解决方法

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

这不是所有权问题,真的;这只是类型不匹配。你没有原始人;您有一个对原始 &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,&char)| {
                    (char,Key::new(...))
                })
            })
            .flatten()
            .collect()
    }

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

    fn build(char_layout: [[char; 10]; 3]) -> HashMap<char,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,Key::new(...))
                })
            })
            .flatten()
            .collect()
    }

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

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

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

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

元组和 Key 拥有该字符的那些副本.to_owned()取得所有权;它会复制一份。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。