使用不可复制的移动值 [E0382] [E0277]

use of moved value, which is non-copyable [E0382] [E0277]

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

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

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

...

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     }

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

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

为什么我的类型是 <core::iter::Map<core::ops::Range<_> 而不是 <core::iter::String>

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

据我了解,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

这里有几个问题。

首先是调用

for f in files { ... }

将按值取 files。这可以通过引用来避免:

for f in &files { ... }

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


其次是files必须是mut,如果是迭代迭代器,则引用&mut。如果你有一些向量,迭代 &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 提供解决方案的问题。