【问题标题】:use of moved value, which is non-copyable [E0382] [E0277]使用不可复制的移动值 [E0382] [E0277]
【发布时间】:2015-11-17 11:22:10
【问题描述】:

我有一个所有权问题,我不太了解。基本上我尝试在我的文件系统上创建一些硬链接,并在创建后删除它们。因此,我创建了一个整数范围,将其映射到我喜欢创建和销毁的实际文件名。我的幼稚解决方案如下所示:

use std::fs;

const src_file: &'static str = "a.txt";
const file_ext: &'static str = ".txt";

fn create_hardlink(dest_file: &str) {
    fs::hard_link(&src_file, &dest_file);
}

fn main() {

    let create = (0..10000).map(|x| x.to_string() + file_ext);
    let remove = (0..10000).map(|x| x.to_string() + file_ext);

    for file in create {
        create_hardlink(&file);
    }

    for file in remove {
        fs::remove_file(&file);
    }
}

但我真正想要完成的是一个解决方案,我不必重复我自己来创建带有文件名的静态集合,并且可以在第二个 for 循环中重用 files

...

fn main() {

    let files = (0..10000).map(|x| x.to_string() + file_ext);

    for file in files {
        create_hardlink(&file);
    }

    for file in files {
        fs::remove_file(&file);
    }
}

所以当我尝试这个时,编译器会抱怨,files 的第二次使用是不可能的,

src/main.rs:20:17: 20:22 error: use of moved value: `files` [E0382]
src/main.rs:20     for file in files {

因为files 已经移入第一个for循环:

src/main.rs:16:17: 16:22 note: `files` moved here because it has type `core::iter::Map<core::ops::Range<i32>, [closure@src/main.rs:14:36: 14:64]>`, which is non-copyable

在阅读了rustc --explain E0382的解释后,我决定将代码更改如下:

...

fn main() {

    let files = Rc::new(RefCell::new((0..10000).map(|x| x.to_string() + file_ext)));

    for file in files.clone() {
        create_hardlink(&file);
    }

    for file in files.clone() {
        fs::remove_file(&file);
    }
}

但这对我来说并不像预期的那样工作:

src/main.rs:16:5: 18:6 error: the trait `core::iter::Iterator` is not implemented for the type `alloc::rc::Rc<core::cell::RefCell<core::iter::Map<core::ops::Range<_>, [closure@src/main.rs:14:53: 14:81]>>>` [E0277]
src/main.rs:16     for file in files.clone() {
src/main.rs:17         create_hardlink(&file);
src/main.rs:18     }
note: in expansion of for loop expansion
src/main.rs:16:5: 18:6 note: expansion site
src/main.rs:16:5: 18:6 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:16:5: 18:6 note: `alloc::rc::Rc<core::cell::RefCell<core::iter::Map<core::ops::Range<_>, [closure@src/main.rs:14:53: 14:81]>>>` is not an iterator; maybe try calling `.iter()` or a similar method
src/main.rs:16     for file in files.clone() {
src/main.rs:17         create_hardlink(&file);
src/main.rs:18     }
note: in expansion of for loop expansion
src/main.rs:16:5: 18:6 note: expansion site
src/main.rs:16:5: 18:6 note: required by `core::iter::IntoIterator::into_iter`
src/main.rs:16     for file in files.clone() {
src/main.rs:17         create_hardlink(&file);
src/main.rs:18     }

我能做什么?我真的必须为alloc::rc::Rc&lt;core::cell::RefCell&lt;core::iter::Map&lt;core::ops::Range&lt;_&gt; 类型实现core::iter::Iterator,就像rustc --explain E0277 告诉我的那样吗?希望不会……

是否有一个简单的解决方案,例如将files 静态定义为staticconst?还是我映射Range 的方法不生锈?

为什么我有一个像&lt;core::iter::Map&lt;core::ops::Range&lt;_&gt; 这样的类型而不是像&lt;core::iter::String&gt; 这样的类型?

我希望你能帮我解决这个问题,并向像我这样的新手启发一点 Rust 所有权原则。

【问题讨论】:

    标签: vector iterator rust clone ownership


    【解决方案1】:

    据我了解,Rust 迭代器只是前向迭代器,因此它们只能迭代一次。您可以collect 将它们放入向量中,也可以使用函数来生成迭代器:

    // 1st option
    let files: Vec<_> = (0..10000).map(|x| x.to_string() + file_ext).collect();
    
    for f in &files { ... } // Borrow `files`
    
    // 2nd option
    let files = || (0..10000).map(|x| x.to_string() + file_ext);
    
    for f in files() { ... } // Call the closure to get an iterator
    

    【讨论】:

    • 我会说第一个选项是 the 惯用的解决方案。第二个选项将导致额外的 10000 个分配,因为该范围将运行两次。
    • 但是第一个会一次分配所有内存,具体取决于文件的数量,也可能不太好。
    • 感谢您的解决方案和 cmets!创建一个宏以避免像这样来自选项2的双重分配是否是一种有价值的方法? macro_rules! files_vec { ( $range:expr ) =&gt; { $range.map(|x| x.to_string() + file_ext).collect() } } 很不灵活,但是已经预编译了
    【解决方案2】:

    这里有几个问题。

    第一个是调用

    for f in files { ... }
    

    将采用 files 的值。这可以通过参考来避免:

    for f in &files { ... }
    

    因为(&amp;foo).into_iter() 有效地解析为foo.iter()


    第二个是files必须是mut,如果你在迭代一个迭代器,引用&amp;mut。如果你有一些向量,迭代&amp;my_vector 是有意义的——你可以在不修改它的情况下迭代它。但是,如果您自己有迭代器,则状态会在迭代器本身中保存和更新。

    let mut files = (0..10000).map(|x| x.to_string() + file_ext);
    
    for file in &mut files {
        create_hardlink(&file);
    }
    
    for file in files {
        fs::remove_file(&file);
    }
    

    第三个是即使你做了这些事情,由于你使用的是单个迭代器,你只能迭代每个元素一次!第二个循环将为空。这是@filmor 提供解决方案的问题。

    【讨论】:

    • 感谢您的解释!正如您指出的那样,我的代码没有迭代器更有意义,因此我接受了@filmor 的答案。
    猜你喜欢
    • 2022-11-27
    • 2021-02-13
    • 2016-01-10
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-13
    相关资源
    最近更新 更多