我正在嘗試制作一個允許異步鏈接副作用的幫助程式,但我無法獲得正確的泛型邊界,以便編譯器理解未來的輸出比用于構建它的參考更持久。
游樂場鏈接
它的要點歸結為:
struct Chain<T> {
data: T
}
impl<T> Chain<T> {
pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
where
Fut: Future<Output=Result<(), E>>,
F: FnOnce(&T) -> Fut
{
todo!()
}
}
給出一個編譯器錯誤
error: lifetime may not live long enough
--> src/main.rs:39:32
|
39 | let r = chain.chain(|this| this.good("bar")).await;
| ----- ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `impl Future` contains a lifetime `'2`
| has type `&'1 MyData`
如果我們修復它chain以便它可以推斷參考在與未來相同的生命周期內可用:
impl<T> Chain<T> {
pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
where
T: 'a,
Fut: 'a Future<Output=Result<(), E>>,
F: FnOnce(&'a T) -> Fut
{
effect(&self.data).await?;
Ok(self.data)
}
}
我們收到一個關于self.data在借用時移動的新編譯器錯誤。
error[E0505]: cannot move out of `self.data` because it is borrowed
--> src/main.rs:30:12
|
23 | pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
| -- lifetime `'a` defined here
...
29 | effect(&self.data).await?;
| ------------------
| | |
| | borrow of `self.data` occurs here
| argument requires that `self.data` is borrowed for `'a`
30 | Ok(self.data)
| ^^^^^^^^^ move out of `self.data` occurs here
我想沿著這條線的病理性關閉|this| futures::future::ready(Err(this))會導致借用仍然“活著”的提前歸還。
問題
我們怎樣才能chain上班?我平時的塊作用域技巧似乎沒有幫助。是否where可以添加一組約束來證明借用和最終移動是在不相交的生命周期內?
uj5u.com熱心網友回復:
在這種特殊情況下,當前的約束語法和缺乏更高級的型別不能讓你表達你想要的。
您可以使用約束較高等級特質,在for<'a>語法,引入一個中間一般壽命引數'a一個內where子句來決定的約束必須是有效的任何壽命。這在這里是必要的,您的第一個修復不起作用的原因是因為'a泛型chain意味著生命周期由呼叫者決定,但是,生命周期的self構造比呼叫者可以選擇的任何生命周期都短。所以稍微更正確的語法(與去糖的原始代碼相同)將是:
pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
where
Fut: Future<Output = Result<(), E>>,
F: for<'a> FnOnce(&'a T) -> Fut
{
...
但這根本沒有幫助,因為Fut和之間仍然沒有關聯'a。不幸的是,無法for<'a>跨多個約束使用相同的方法。您可以嘗試使用impl Trait一次定義它,但不支持:
pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> 'a)
{
...
error[E0562]: `impl Trait` not allowed outside of function and method return types
--> src/lib.rs:35:44
|
35 | where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> 'a)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
希望將來可以更好地支持更高級的型別。通過使用幾乎完整的通用關聯型別功能,這個特殊案例可能在每晚都有一個解決方案,但我還沒有找到它。
所以唯一真正的解決方法是使用命名型別作為回傳值,這實際上只給我們留下了 trait 物件:
use std::pin::Pin;
use futures::future::FutureExt;
pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
where F: for<'a> FnOnce(&'a T) -> Pin<Box<dyn Future<Output = Result<(), E>> 'a>>
{
...
let r = chain.chain(|this| this.good("bar").boxed()).await;
作為旁注,您的bad案例仍然無法編譯并且確實無法作業,因為您將回傳對本地值的參考。
uj5u.com熱心網友回復:
看起來您正在嘗試實施future.then()
如果您意識到這一點并且將其作為練習來進行,則您可能應該以效果方法回傳值的方式設計它,并使用這些值從鏈方法回傳。這樣你就可以強制執行正確的操作順序。據我了解您的設計,您不會從等待鏈方法內的效果中受益,因為您的跳過函式也是異步的并且將回傳未來(鏈方法的實際回傳型別是 Future<Output=Result<T, E>>,因為 async 就是這樣作業的:它會在將來包裝您的顯式回傳型別)。
因此,在鏈內等待效果是沒有意義的,無論何時使用它,您仍然必須等待它-并且在您真正在鏈外等待它之前什么都不會發生-期貨就是這樣懶惰的。
TL;DR 我會安排你的效果方法來回傳值并安排鏈來只回傳這些值
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/401248.html
上一篇:DynamoDB異步REST呼叫
