关闭生命周期的麻烦

时间:2018-04-23 01:20:04

标签: rust closures lifetime

我真的在关闭生命周期中苦苦挣扎。我正在做一个简单的刽子手游戏,其中所有连接都玩一个游戏。我试图将一个闭包传递给一个更新游戏然后广播JSON的频道,但我遇到了终身问题。

extern crate names;
extern crate ws;

#[macro_use]
extern crate json;

use names::{Generator, Name};
use std::collections::HashSet;
use std::sync::mpsc;
use std::thread;
use ws::{listen, CloseCode, Handler, Message, Result, Sender};

type Job = Box<FnMut(&mut Game) + Send>;

#[derive(Debug)]
struct Game {
    word: Vec<String>,
    guesses: HashSet<String>,
    misses: u32,
    progress: Vec<String>,
}

impl Game {
    fn increment_miss(&mut self) {
        self.misses += 1;
    }

    fn update_progress(&mut self, guess: &String) {
        for (i, letter) in self.word.iter().enumerate() {
            if letter == guess {
                self.progress[i] = letter.clone();
            }
        }
    }

    fn status(&self) -> &str {
        if self.misses > 10 {
            "lose"
        } else if self.progress == self.word {
            "win"
        } else {
            "active"
        }
    }
}

struct Server {
    out: Sender,
    tx: std::sync::mpsc::Sender<Job>,
}

impl Handler for Server {
    fn on_message(&mut self, msg: Message) -> Result<()> {
        let string_msg = msg.to_string();

        self.tx
            .send(Box::new(move |mut game: &mut Game| {
                if game.guesses.insert(string_msg.clone()) {
                    check_letter(&mut game, &string_msg);
                };

                let status = game.status();

                let progress = game.progress.clone();
                let guesses = game.guesses.clone().into_iter().collect::<Vec<String>>();
                println!(
                    "guesses: {:?}, progress: {:?}, misses: {}, status: {}",
                    guesses, progress, game.misses, status
                );

                self.out.broadcast(json::stringify(object!{
                    "status"  => "status",
                    "progress" => "progress",
                    "guesses" => "guesses",
                    "misses" => "misses",
                }));
            }))
            .unwrap();

        Ok(())
    }

    fn on_close(&mut self, code: CloseCode, reason: &str) {
        match code {
            CloseCode::Normal => println!("The client is done with the connection."),
            CloseCode::Away => println!("The client is leaving the site."),
            _ => println!("The client encountered an error: {}", reason),
        }
    }
}

fn check_letter(game: &mut Game, guess: &String) {
    if game.word.contains(guess) {
        game.update_progress(guess);
    } else {
        game.increment_miss();
    }
}

fn generate_word() -> Vec<String> {
    let mut generator = Generator::with_naming(Name::Plain);
    generator
        .next()
        .unwrap()
        .split("")
        .map(|c| c.to_string())
        .filter(|s| s != "")
        .collect::<Vec<String>>()
}

fn start_game() -> Game {
    let word = generate_word();

    Game {
        progress: vec!["".to_string(); word.len()],
        word: word,
        guesses: HashSet::new(),
        misses: 0,
    }
}

fn main() {
    let (tx, rx) = mpsc::channel::<Job>();

    thread::spawn(move || {
        let mut game = start_game();
        for mut received in rx {
            received(&mut game)
        }
    });

    listen("127.0.0.1:3000", |out| Server {
        out: out,
        tx: mpsc::Sender::clone(&tx),
    }).unwrap();
}

我收到以下错误:

Compiling hang_man v0.1.0 (file:///Users/smykowski/workspace/rust/hang_man)
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:57:22
   |
57 |           self.tx.send(Box::new(move |mut game: &mut Game| {
   |  ______________________^
58 | |             if game.guesses.insert(string_msg.clone()) {
59 | |                 check_letter(&mut game, &string_msg);
60 | |             };
...  |
75 | |             }));
76 | |         })).unwrap();
   | |__________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 54:5...
  --> src/main.rs:54:5
   |
54 | /     fn on_message(&mut self, msg: Message) -> Result<()> {
55 | |         let string_msg = msg.to_string();
56 | |
57 | |         self.tx.send(Box::new(move |mut game: &mut Game| {
...  |
78 | |         Ok(())
79 | |     }
   | |_____^
note: ...so that the type `[closure@src/main.rs:57:31: 76:10 string_msg:std::string::String, self:&mut Server]` will meet its required lifetime bounds
  --> src/main.rs:57:22
   |
57 |           self.tx.send(Box::new(move |mut game: &mut Game| {
   |  ______________________^
58 | |             if game.guesses.insert(string_msg.clone()) {
59 | |                 check_letter(&mut game, &string_msg);
60 | |             };
...  |
75 | |             }));
76 | |         })).unwrap();
   | |__________^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<for<'r> std::ops::FnMut(&'r mut Game) + std::marker::Send + 'static>, found std::boxed::Box<for<'r> std::ops::FnMut(&'r mut Game) + std::marker::Send>)
  --> src/main.rs:57:22
   |
57 |           self.tx.send(Box::new(move |mut game: &mut Game| {
   |  ______________________^
58 | |             if game.guesses.insert(string_msg.clone()) {
59 | |                 check_letter(&mut game, &string_msg);
60 | |             };
...  |
75 | |             }));
76 | |         })).unwrap();
   | |__________^

1 个答案:

答案 0 :(得分:0)

这里有一些混乱,因为Rust对生命的某些特征并不十分清楚。在这个例子中,

  1. 封闭的生命周期受到移入其中的所有参数的生命周期的限制。在您的情况下,由于行self,闭包受self.out.broadcast限制。请注意,self实际上是来自&selffn on_message参数的引用。基本上你创建类似

    的东西
    Box<FnMut(&mut Game) + Send + 'a>
    

    'a的生命周期为&self

  2. 创建盒装特征对象时,默认生命周期为'static。这意味着

    type Job = Box<FnMut(&mut Game) + Send> = Box<FnMut(&mut Game) + Send + 'static>
    

    为避免这种情况,您可以自我克隆并在此闭包内移动self.clone()