使用期货时,Either是否有更符合人体工程学的语法? [英] Is there a more ergonomic syntax for Either when using futures?

查看:96
本文介绍了使用期货时,Either是否有更符合人体工程学的语法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是使用Tokio运行返回未来的函数的示例:

Here's an example of using Tokio to run a function that returns a future:

use futures::sync::oneshot;
use futures::Future;
use std::thread;
use std::time::Duration;
use tokio;

#[derive(Debug)]
struct MyError {
    error_code: i32,
}

impl From<oneshot::Canceled> for MyError {
    fn from(_: oneshot::Canceled) -> MyError {
        MyError { error_code: 1 }
    }
}

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        sx.send(100).unwrap();
    });
    return rx.map_err(|e| MyError::from(e));
}

fn main() {
    tokio::run(deferred_task().then(|r| {
        println!("{:?}", r);
        Ok(())
    }));
}

但是,当所讨论的函数(即deferred_task)不是平凡的时,我编写代码时代码就会变得更加复杂,因为?操作似乎不容易与返回未来混合:

However, when the function in question (i.e. deferred_task) is non-trivial, the code becomes much more complex when I write it, because the ? operation doesn't seem to easily mix with returning a future:

fn send_promise_to_worker(sx: oneshot::Sender<i32>) -> Result<(), ()> {
    // Send the oneshot somewhere in a way that might fail, eg. over a channel
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        sx.send(100).unwrap();
    });
    Ok(())
}

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    send_promise_to_worker(sx)?; // <-------- Can't do this, because the return is not a result
    return rx.map_err(|e| MyError::from(e));
}

A Future 一个Result,将其包装在结果中毫无意义,并且破坏了impl Future返回类型.

A Future is a Result, it's meaningless to wrap it in result, and it breaks the impl Future return type.

相反,您会得到一个深层嵌套的链:

Instead you get a deeply nested chain of:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    match query_data() {
        Ok(_i) => match send_promise_to_worker(sx) {
            Ok(_) => Either::A(rx.map_err(|e| MyError::from(e))),
            Err(_e) => Either::B(futures::failed(MyError { error_code: 2 })),
        },
        Err(_) => Either::B(futures::failed(MyError { error_code: 2 })),
    }
}

完整代码

结果越多,嵌套就越深;完全是?运算符正常解决的问题.

The more results you have, the deeper the nesting; exactly what the ? operator solves normally.

我错过了什么吗?是否有一些语法糖可以简化此过程?

Am I missing something? Is there some syntax sugar to make this easier?

推荐答案

我看不到async/await语法如何对Either有所帮助.最终,您仍然需要返回单个具体类型,而这正是Either所提供的. async/await将减少对Future::mapFuture::and_then之类的组合器的需求.

I do not see how async / await syntax will categorically help you with Either. Ultimately, you still need to return a single concrete type, and that's what Either provides. async / await will reduce the need for combinators like Future::map or Future::and_then however.

另请参阅:

话虽如此,您无需在此处使用Either.

That being said, you don't need to use Either here.

您具有连续的Result返回函数,因此您可以借用JavaScript的技巧,并使用 IIFE 可以使用?运算符使用.然后,我们可以将合并后的Result提升"到将来,并与接收者的将来链接起来:

You have consecutive Result-returning functions, so you can borrow a trick from JavaScript and use an IIFE to use use the ? operator. Then, we can "lift up" the combined Result into a future and chain it with the future from the receiver:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (tx, rx) = oneshot::channel();

    let x = (|| {
        let _i = query_data().map_err(|_| MyError { error_code: 1 })?;
        send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
        Ok(())
    })();

    future::result(x).and_then(|()| rx.map_err(MyError::from))
}

据我所知,将来IIFE可以替换为try块.

In the future, that IIFE could be replaced with a try block, as I understand it.

您还可以采用另一种方法,将所有内容转换为未来:

You could also go the other way and convert everything to a future:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (tx, rx) = oneshot::channel();

    query_data()
        .map_err(|_| MyError { error_code: 1 })
        .into_future()
        .and_then(|_i| {
            send_promise_to_worker(tx)
                .map_err(|_| MyError { error_code: 2 })
                .into_future()
        })
        .and_then(|_| rx.map_err(MyError::from))
}

使用async/await语法可以帮助 :

async fn deferred_task() -> Result<i32, MyError> {
    let (tx, rx) = oneshot::channel();

    query_data().map_err(|_| MyError { error_code: 1 })?;

    send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;

    let v = await! { rx }?;

    Ok(v)
}

通过在Future特征中添加leftright方法,我还看到了构造Either的改进语法:

I have also seen improved syntax for constructing the Either by adding left and right methods to the Future trait:

foo.left();
// vs
Either::left(foo);

但是,这在当前的任何实现中都没有出现.

However, this doesn't appear in any of the current implementations.

A Future 一个Result

不,不是.

有两个相关的Future可以讨论:

There are two relevant Futures to talk about:

  • From the futures 0.1 crate
  • From the (nightly) standard library

值得注意的是,Future::poll返回的类型可以处于两种状态:

Notably, Future::poll returns a type that can be in two states:

  • 完成
  • 不完整

在期货箱中,成功"和失败"与完整"相关,而在标准库中则不相关.在箱子中,Result实现 ,并且在标准库中,您可以使用 future::ready .这两个都允许将Result转换为Future,但这并不意味着Result 的未来,无非就是说Vec<u8>是迭代器,即使它是迭代器也是如此.可以转换为一个.

In the futures crate, "success" and "failure" are tied to "complete", whereas in the standard library they are not. In the crate, Result implements IntoFuture, and in the standard library you can use future::ready. Both of these allow converting a Result into a future, but that doesn't mean that Result is a future, no more than saying that a Vec<u8> is an iterator, even though it can be converted into one.

?运算符(由Try特性支持)可能会得到增强,以自动从Result转换为特定类型的Future,或者Result甚至会实现Future直接,但我还没有听说过任何这样的计划.

It's possible that the ? operator (powered by the Try trait), will be enhanced to automatically convert from a Result to a specific type of Future, or that Result will even implement Future directly, but I have not heard of any such plans.

这篇关于使用期货时,Either是否有更符合人体工程学的语法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆