我的代码中的非法移动在哪里?

Where's the illegal move in my code?

我在移动不应该移动的 (a) 个值时遇到错误,但错误并没有说明我不小心移动了什么,我错过了它当我只看代码时。

错误:

-*- mode: compilation; default-directory: "~/Developer/Rust/optimal_subset_finder/src/" -*-
Compilation started at Thu Aug 27 21:53:26

cargo build
   Compiling optimal_subset_finder v0.1.1 (file:///Users/camdennarzt/Developer/Rust/optimal_subset_finder)
main.rs:66:23: 98:10 error: cannot move out of captured outer variable in an `FnMut` closure
main.rs:66         thread::spawn(move || {
main.rs:67             let mut best_set : Vec<String> = Vec::new();
main.rs:68             let mut best_count = 0;
main.rs:69             let mut rng = thread_rng();
main.rs:70             let mut indices = Vec::new();
main.rs:71             let limit = attempts.clone()/num_cpus::get();
           ...
note: in expansion of closure expansion
main.rs:66:23: 98:10 note: expansion site
note: in expansion of closure expansion
main.rs:63:57: 99:6 note: expansion site
main.rs:66:23: 98:10 error: cannot move out of captured outer variable in an `FnMut` closure
main.rs:66         thread::spawn(move || {
main.rs:67             let mut best_set : Vec<String> = Vec::new();
main.rs:68             let mut best_count = 0;
main.rs:69             let mut rng = thread_rng();
main.rs:70             let mut indices = Vec::new();
main.rs:71             let limit = attempts.clone()/num_cpus::get();
           ...
note: in expansion of closure expansion
main.rs:66:23: 98:10 note: expansion site
note: in expansion of closure expansion
main.rs:63:57: 99:6 note: expansion site
error: aborting due to 2 previous errors
Could not compile `optimal_subset_finder`.

To learn more, run the command again with --verbose.

Compilation exited abnormally with code 101 at Thu Aug 27 21:53:27

代码:

extern crate rand;
extern crate csv;
extern crate num_cpus;
extern crate rustc_serialize;
extern crate docopt;

use std::fs::File;
use csv::Reader;
use rand::{thread_rng, sample};
use std::thread;
use std::io::{self, Write};
use std::sync::{Arc, Mutex};
use docopt::Docopt;
use std::collections::HashSet;

fn as_bool(rdr:&mut Reader<File>)->Vec<Vec<bool>>{
    rdr.records().map(|r|{
        r.unwrap().iter().skip(1).map(|r|{
            r == "TRUE"
        }).collect()
    }).collect()
}

fn as_strings(rdr:&mut Reader<File>)->Vec<String>{
    rdr.records().map(|r|{
        r.unwrap()[0].clone()
    }).collect()
}

fn met_n_in_common(n:usize,csv:&Vec<Vec<bool>>)->bool{
    csv.iter().all(|r|r[n])
}

fn mets_in_common(csv:&Vec<Vec<bool>>)->usize {
    (0..csv[0].len()).filter(|i| met_n_in_common(*i,csv)).count()
}

const USAGE: &'static str = "
Usage:
  ./optimal_subset_finder PATH ATTEMPTS
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_PATH: String,
    arg_ATTEMPTS: usize,
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
    let attempts = args.arg_ATTEMPTS.clone();

    let mut csv = csv::Reader::from_file(args.arg_PATH.clone()).unwrap();
    let data = as_bool(&mut csv);
    csv.seek(0);
    let files = as_strings(&mut csv);

    let tried_indices = Arc::new(Mutex::new(HashSet::new()));
    let results = Arc::new(Mutex::new(Vec::new()));

    let mut threads : Vec<_> = (0..num_cpus::get()).map(|i|{
        let tried_indices = tried_indices.clone();
        let results = results.clone();
        thread::spawn(move || {
            let mut best_set : Vec<String> = Vec::new();
            let mut best_count = 0;
            let mut rng = thread_rng();
            let mut indices = Vec::new();
            let limit = attempts.clone()/num_cpus::get();

            for _ in (0..limit) {
                {
                    let mut tried_indices = tried_indices.lock().unwrap();
                    while {
                        let count = *sample(&mut rng, 13..83, 1).first().unwrap();
                        indices = sample(&mut rng, 0..83, count);
                        tried_indices.contains(&indices)
                    }{}
                    tried_indices.insert(indices.to_owned());
                };
                let current_set:Vec<_> = {
                    indices.iter().map(|i|{
                        files[*i].clone()
                    }).collect()
                };
                let current_count = mets_in_common(&data);
                if (current_count > best_count){
                    best_count = current_count;
                    best_set = current_set;
                }
            }
            {
                let mut results = results.lock().unwrap();
                results.push((best_count,best_set));
            }
        })
    }).collect();
    for t in threads.into_iter() {
        t.join();
    }
    {
        let mut results = results.lock().unwrap().to_owned();
        let first = results.pop().unwrap();
        let (count,set) = results.into_iter().fold(first,|a,r| if(a.0 < r.0){r}else{a});
        println!("results {:?} {:?}",count,set);
    }
}

因为 move 闭包是如何在另一个闭包内部而不是在外层的(例如,如果你有一个 for 循环推送到一个向量),错误消息实际上不是非常好,但是如果删除 move 关键字,您可以获得更好的错误消息,大致如下:

<anon>:42:23: 71:10 error: closure may outlive the current function, but it borrows `tried_indices`, which is owned by the current function [E0373]
<anon>:42         thread::spawn(|| {
<anon>:43             let mut best_set : Vec<String> = Vec::new();
<anon>:44             let mut best_count = 0;
<anon>:45             let mut indices: Vec<usize> = Vec::new();
<anon>:46             let limit = 1;
<anon>:47 
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:50:45: 50:58 note: `tried_indices` is borrowed here
<anon>:50                     let mut tried_indices = tried_indices.lock().unwrap();
                                                      ^~~~~~~~~~~~~
note: in expansion of for loop expansion
<anon>:48:13: 66:14 note: expansion site
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 help: to force the closure to take ownership of `tried_indices` (and any other referenced variables), use the `move` keyword, as shown:
<anon>:           thread::spawn(move || {
<anon>:               let mut best_set : Vec<String> = Vec::new();
<anon>:               let mut best_count = 0;
<anon>:               let mut indices: Vec<usize> = Vec::new();
<anon>:               let limit = 1;
<anon>:   
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 error: closure may outlive the current function, but it borrows `**files`, which is owned by the current function [E0373]
<anon>:42         thread::spawn(|| {
<anon>:43             let mut best_set : Vec<String> = Vec::new();
<anon>:44             let mut best_count = 0;
<anon>:45             let mut indices: Vec<usize> = Vec::new();
<anon>:46             let limit = 1;
<anon>:47 
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:58:25: 58:30 note: `**files` is borrowed here
<anon>:58                         files[*i].clone()
                                  ^~~~~
note: in expansion of closure expansion
<anon>:57:40: 59:22 note: expansion site
note: in expansion of for loop expansion
<anon>:48:13: 66:14 note: expansion site
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 help: to force the closure to take ownership of `**files` (and any other referenced variables), use the `move` keyword, as shown:
<anon>:           thread::spawn(move || {
<anon>:               let mut best_set : Vec<String> = Vec::new();
<anon>:               let mut best_count = 0;
<anon>:               let mut indices: Vec<usize> = Vec::new();
<anon>:               let limit = 1;
<anon>:   
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 error: closure may outlive the current function, but it borrows `**data`, which is owned by the current function [E0373]
<anon>:42         thread::spawn(|| {
<anon>:43             let mut best_set : Vec<String> = Vec::new();
<anon>:44             let mut best_count = 0;
<anon>:45             let mut indices: Vec<usize> = Vec::new();
<anon>:46             let limit = 1;
<anon>:47 
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:61:53: 61:57 note: `**data` is borrowed here
<anon>:61                 let current_count = mets_in_common(&data);
                                                              ^~~~
note: in expansion of for loop expansion
<anon>:48:13: 66:14 note: expansion site
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 help: to force the closure to take ownership of `**data` (and any other referenced variables), use the `move` keyword, as shown:
<anon>:           thread::spawn(move || {
<anon>:               let mut best_set : Vec<String> = Vec::new();
<anon>:               let mut best_count = 0;
<anon>:               let mut indices: Vec<usize> = Vec::new();
<anon>:               let limit = 1;
<anon>:   
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 error: closure may outlive the current function, but it borrows `results`, which is owned by the current function [E0373]
<anon>:42         thread::spawn(|| {
<anon>:43             let mut best_set : Vec<String> = Vec::new();
<anon>:44             let mut best_count = 0;
<anon>:45             let mut indices: Vec<usize> = Vec::new();
<anon>:46             let limit = 1;
<anon>:47 
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:68:35: 68:42 note: `results` is borrowed here
<anon>:68                 let mut results = results.lock().unwrap();
                                            ^~~~~~~
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
<anon>:42:23: 71:10 help: to force the closure to take ownership of `results` (and any other referenced variables), use the `move` keyword, as shown:
<anon>:           thread::spawn(move || {
<anon>:               let mut best_set : Vec<String> = Vec::new();
<anon>:               let mut best_count = 0;
<anon>:               let mut indices: Vec<usize> = Vec::new();
<anon>:               let limit = 1;
<anon>:   
          ...
note: in expansion of closure expansion
<anon>:42:23: 71:10 note: expansion site
note: in expansion of closure expansion
<anon>:39:43: 72:6 note: expansion site
error: aborting due to 4 previous errors

这说明有四个变量被捕获,说明其中两个在想按值捕获的时候出问题了;他们是 filesdata.

两者都是向量,只能从一个线程访问的东西。而thread::spawn要求它拥有所有的数据;借用检查器无法推断出在 datafiles 被释放之前加入你的守卫。