我想以用Rust编写的轻量,长寿命线程的形式实现用户交互脚本。在脚本中,我有一些要异步等待用户输入的地方。
在JavaScript中,我将使用生成器,在该生成器中您可以传递问题,并获取答案,例如:
function* my_scenario() {
yield "What is your name?";
let my_name = yield "How are you feeling?";
let my_mood = yield "";
...
}
let my_session = my_scenario();
...
my_session.next("Peter");
my_session.next("happy");
但是,Rust的生成器方法resume()
不包含任何参数!我无法克隆生成器或从函数返回它,以使许多用户会话具有不同的状态。我想到了使用async fn()
而不是生成器,但是我不明白如何在每个步骤中调用它,并在其中传递值。
答案 0 :(得分:3)
yield
的返回值实际上只是另一个已隐式传递给第一个生成器的生成器,除了它迫使两者以奇怪的方式绑定在一起。
您可以在原始代码中用垃圾yield ""
看到,即使没有任何可返回的内容,您也需要获取值。此外,您的示例还要求生成器的用户在提出问题之前先知道问题的答案 ,这似乎很不合常规。
明确传递第二个生成器:
#![feature(generators, generator_trait)]
use std::{
io,
ops::{Generator, GeneratorState},
};
fn user_input() -> impl Generator<Yield = String> {
|| {
let input = io::stdin();
loop {
let mut line = String::new();
input.read_line(&mut line).unwrap();
yield line;
}
}
}
fn my_scenario(
input: impl Generator<Yield = String>,
) -> impl Generator<Yield = &'static str, Return = String> {
|| {
let mut input = Box::pin(input);
yield "What is your name?";
let my_name = match input.as_mut().resume() {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
yield "How are you feeling?";
let my_mood = match input.as_mut().resume() {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario(user_input());
let mut my_session = Box::pin(my_session);
loop {
match my_session.as_mut().resume() {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
}
}
$ cargo run
What is your name?
Shep
How are you feeling?
OK
Shep is OK
您也可以提供硬编码数据:
let user_input = || {
yield "Peter".to_string();
yield "happy".to_string();
};
let my_session = my_scenario(user_input);
答案 1 :(得分:0)
大约在Rust每晚2020-02-08为止,Rust的生成器现在接受resume
的参数,与原始JavaScript示例更加匹配:
#![feature(generators, generator_trait)]
use std::{
io::{self, BufRead},
ops::{Generator, GeneratorState},
};
fn my_scenario() -> impl Generator<String, Yield = &'static str, Return = String> {
|_arg: String| {
let my_name = yield "What is your name?";
let my_mood = yield "How are you feeling?";
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario();
let mut my_session = Box::pin(my_session);
let stdin = io::stdin();
let mut lines = stdin.lock().lines();
let mut line = String::new();
loop {
match my_session.as_mut().resume(line) {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
line = lines.next().expect("User input ended").expect("User input malformed");
}
}