使用期货时,Either是否有更符合人体工程学的语法? [英] Is there a more ergonomic syntax for Either when using futures?
问题描述
下面是使用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::map
或Future::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
特征中添加left
和right
方法,我还看到了构造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 Future
s 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屋!