Как реализовать облегченный долгоживущий поток на основе генератора или асинхронной функции в Rust? - PullRequest
1 голос
/ 02 июня 2019

Я хочу реализовать скрипт взаимодействия с пользователем в виде облегченного, долгоживущего потока, написанного на 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(), но я не понимаю, как вызывать его на каждом шаге, передавая туда значение.

1 Ответ

3 голосов
/ 03 июня 2019

Возвращаемое значение из 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...