如何在 Rust 中将 Future 的生命周期绑定到 fn 参数

2023-11-29

我想写一个简单的run_transactionRust MongoDB 驱动程序的函数

该函数尝试通过 mongo db 客户端执行事务,并在遇到可重试错误时重试该事务

这是该函数的最小可重现示例。

use mongodb::{Client, Collection, ClientSession};
use mongodb::bson::Document;
use std::future::Future;

pub enum Never {}

fn main() {
    run_transaction(|mut session| async move {
        let document = collection().find_one_with_session(None,  None, &mut session).await?.unwrap();
        let r: Result<Document, TransactionError<Never>> = Ok(document);
        return r;
    });
}

fn collection() -> Collection<Document> {
    unimplemented!();
}

fn client() -> Client {
    unimplemented!();
}

pub enum TransactionError<E> {
    Mongodb(mongodb::error::Error),
    Custom(E)
}

impl<T> From<mongodb::error::Error> for TransactionError<T> {
    fn from(e: mongodb::error::Error) -> Self {
        TransactionError::Mongodb(e)
    }
}

// declaration
pub async fn run_transaction<T, E, F, Fut>(f: F) -> Result<T, TransactionError<E>> 
  where for<'a>
        F: Fn(&'a mut ClientSession) -> Fut + 'a,
        Fut: Future<Output = Result<T, TransactionError<E>>> { 

  
  let mut session = client().start_session(None).await?;
  session.start_transaction(None).await?;
      
  'run: loop {
    let r = f(&mut session).await;

    match r {
      Err(e) => match e {
        TransactionError::Custom(e) => return Err(TransactionError::Custom(e)),
        TransactionError::Mongodb(e) => {
          if !e.contains_label(mongodb::error::TRANSIENT_TRANSACTION_ERROR) {
            return Err(TransactionError::Mongodb(e));
          } else {
            continue 'run;
          }
        }
      },

      Ok(v) => {
        'commit: loop {
          match session.commit_transaction().await {
            Ok(()) => return Ok(v),
            Err(e) => {
              if e.contains_label(mongodb::error::UNKNOWN_TRANSACTION_COMMIT_RESULT) {
                continue 'commit;
              } else {
                return Err(TransactionError::Mongodb(e))
              }
            }
          }
        }
      }
    }
  }
}

但借用检查员不断抱怨此消息:

error: lifetime may not live long enough
  --> src/main.rs:8:35
   |
8  |       run_transaction(|mut session| async move {
   |  ______________________------------_^
   | |                      |          |
   | |                      |          return type of closure `impl Future` contains a lifetime `'2`
   | |                      has type `&'1 mut ClientSession`
9  | |         let document = collection().find_one_with_session(None,  None, &mut session).await?.unwrap();
10 | |         let r: Result<Document, TransactionError<Never>> = Ok(document);
11 | |         return r;
12 | |     });
   | |_____^ returning this value requires that `'1` must outlive `'2`

我有办法解决这个问题吗?


你真正需要的是这样的:

pub async fn run_transaction<T, E, F, Fut>(f: F) -> Result<T, TransactionError<E>> 
  where
    for<'a>
        F: Fn(&'a mut ClientSession) -> Fut,
        Fut: Future<Output = Result<T, TransactionError<E>>> + 'a { 

不幸的是,这不起作用,因为中定义的“较高等级特征界限”(HRTB)for<'a>只适用于下一个界限,而不适用于每一个界限,并且没有办法连接两个生命周期......

但并不是一切都失去了!我找到了这个question在 Rust 支持论坛中遇到类似问题,可以根据您的问题进行调整。基本思想是你创建一个特征来包装FnFuture具有相同生命周期的界限:

pub trait XFn<'a, I: 'a, O> {
  type Output: Future<Output = O> + 'a;
  fn call(&self, session: I) -> Self::Output;
}

impl<'a, I: 'a, O, F, Fut> XFn<'a, I, O> for F
where
  F: Fn(I) -> Fut,
  Fut: Future<Output = O> + 'a,
{
  type Output = Fut;
  fn call(&self, x: I) -> Fut {
      self(x)
  }
}

现在你的绑定函数很简单:

pub async fn run_transaction<T, E, F>(f: F) -> Result<T, TransactionError<E>>
  where for<'a>
        F: XFn<'a, &'a mut ClientSession, Result<T, TransactionError<E>>>

请记住,要调用您必须编写的函数f.call(&mut session).

不幸的是,电话run_transaction(),因为它是,不编译,说实施FnOnce不够通用。我认为这是一个限制/错误async move因为异步闭包不稳定。但您可以使用适当的异步函数:

    async fn do_the_thing(session: &mut ClientSession) -> Result<Document, TransactionError<Never>> {
      let document = collection().find_one_with_session(None,  None, session).await?.unwrap();
      let r: Result<Document, TransactionError<Never>> = Ok(document);
      return r;
    }
    run_transaction(do_the_thing).await;

如果您认为这太复杂,并且不介意支付非常小的运行时价格,那么还有一个更简单的选择:您可以将返回的 future 装箱,完全避免第二个泛型:

pub async fn run_transaction<T, E, F>(f: F) -> Result<T, TransactionError<E>>
  where for<'a>
        F: Fn(&'a mut ClientSession) -> Pin<Box<dyn Future<Output = Result<T, TransactionError<E>>> + 'a>>

然后,调用它:

    run_transaction(|mut session| Box::pin(async move {
        let document = collection().find_one_with_session(None,  None, session).await?.unwrap();
        let r: Result<Document, TransactionError<Never>> = Ok(document);
        return r;
    }));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Rust 中将 Future 的生命周期绑定到 fn 参数 的相关文章

随机推荐

  • 按比例调整图像大小

    我想将上传的图像调整为宽度 180px 高度成比例 有没有任何课程可以做到这一点 感谢帮助 我认为这个问题可以用实际的代码示例来回答 下面的代码向您展示了如何调整目录中图像的大小uploaded 并将调整大小的图像保存在文件夹中resize
  • iphone 平铺图像

    我有一个 1280 x 1664 的图像 我想使用 Apple 提供的 ScrollViewSuite 示例 但我需要将此图像剪切到多个缩放级别 那么 有人知道如何在多个缩放级别上平铺图像吗 一定有一种简单的方法可以将图像填充到 256x2
  • 处理交易时虚拟机异常:气体耗尽

    我正在使用 testrpc web3 1 0 和 Solidity 构建一个简单的 Dapp 但我总是收到此错误 并且找不到问题所在 请帮忙 我的 JavaScript 文件 const Web3 require web3 const fs
  • 如何在 Kotlin Desktop Compose 中触发 PC 键盘输入

    我将使用 Kotlin Jetpack Compose 开发一个 POS 系统 我想知道如何在我的项目中触发键盘输入事件 在 Compose Desktop 中 您可以使用以下方式侦听关键事件onKeyEvent Window范围 Wind
  • Discord Bot + Google Cloud Run:“容器无法启动。”

    我正在尝试部署一个用 python 编写并通过 Docker 容器化到 Google Cloud Run 的简单 Discord 机器人 Dockerfile 非常简单 FROM python 3 9 WORKDIR app COPY re
  • 有没有简单的 3 列、纯 CSS 布局?

    不需要以下内容的一种 对图像的依赖 即 假柱 专门为 IE 添加的某种怪异或 hack 要求 IE 以怪异模式运行 没有像三个 DIV 之一与其他 DIV 重叠那样的奇怪现象 即 圣杯 边距设置为高负数 使它们远离视图屏幕 再次 圣杯 布局
  • ember.js #每个按属性排序

    我有一个数组Ember Object由 Handlebars 显示的 s each 我希望每次数组更改时都按这些对象的属性进行排序 所以像这样 var arr Ember Objects create position 0 label fo
  • EF 上的数据库连接错误

    我对实体框架非常陌生 我在编写基于 Web api 的网站 连接到 mssql 时遇到问题 我不断收到看似随机的错误 大部分似乎与数据库相关 这些错误最常在网站首次发布时发生 但有时也会在距上次发布数小时后发生 错误的选择 无效操作 连接已
  • 如何使用警报管理器重复我的服务?

    我做了一个基于位置的应用程序 现在我需要每 30 分钟运行一次定位服务 为此 我尝试使用警报管理器 但它只运行我的服务一次 我需要它每 30 分钟运行一次吗 My code gps back process java public clas
  • C# 转换整个数组?

    我看到这个Array ConvertAll方法 但是需要一个Converter作为一个论点 当我已经在类中定义了一个隐式转换器时 我不明白为什么需要一个转换器 public static implicit operator Vec2 Poi
  • C++:如何添加外部库

    我正在尝试将 SVL 添加到我的项目中 如果没有它 我会收到数百个错误 未定义的参考 添加 lSVL 后 所有错误都消失了 但 gcc 说 找不到 lSVL 其他一切 SDL SDL TTF SDL Mixer 都工作正常 您应该告知 gc
  • JQuery UI Draggable如何获取拖动开始位置

    我可以拖动我的可拖动项目并将它们放在我的可放置物品上 并从中获取放置位置 ui absolutePosition 发生在放置事件上 我需要知道拖动从哪个位置开始 我尝试在拖动开始事件上获取 ui position 但这是空的 我也尝试过 拖
  • 在 Google Apps 脚本中创建动态下拉列表[关闭]

    Closed 这个问题需要多问focused 目前不接受答案 我想使用 Google Apps 脚本动态更改 Google 电子表格的单元格验证功能中候选列表的值 我需要从其他电子表格中获取值 如果我理解正确的话 这是我一直在努力解决的问题
  • Visual Studio代码nodejs请求“启动”:无法启动目标(原因:生成节点ENOENT)

    当我使用 Visual Studio 代码调试 NodeJS 应用程序时 视觉工作室代码告诉我request launch cannot launch target reason spawn node ENOENT 我的nodejs版本是4
  • 使用循环 onclick 更改背景颜色

    这是我的 js 小提琴 http jsfiddle net pYM38 16 var box document getElementById box var colors purple yellow orange brown black b
  • 确定 Android 设备是否以编程方式 root? [复制]

    这个问题在这里已经有答案了 可能的重复 确定是否在已取得 root 权限的设备上运行 如何 以编程方式 确定 Android 设备是否 已 root 运行软件或 ROM 的破解副本 我的数据库中有一些敏感信息 我想在手机 root 后 即用
  • SSKeychain 不保留数据

    我在用SSKeychain用于存储设备的UUID 然而 有时SSKeychain不保留 UUID 因此我的应用程序必须重新创建新的 UUID 有人可以告诉我一些原因吗SSKeychain不保留数据 提前致谢 对不起我的英语不好 在 Obje
  • SmartGit:文件或目录不是过期错误消息吗?

    有人请帮助我理解我对下面发生的事情的错误 误解 这是一个 SVN 存储库 正如您所看到的 我有两个分支并对版本进行了一些独立的更改 根据上面的日志 两个服务器分支都映射回当前本地发布分支 该分支提前了一些提交 如果我去将发布的更改推送回远程
  • 组合 SQL Server 查询

    我正在使用 SQL Server 并且有两个表 我想将其合并为一个查询 以用于填充网格视图 表1 dbo 工作UID PK 整数 Tech Ticket 整数 RMA Ticket 整数 区域 nchar10 已完成 nchar10 FA
  • 如何在 Rust 中将 Future 的生命周期绑定到 fn 参数

    我想写一个简单的run transactionRust MongoDB 驱动程序的函数 该函数尝试通过 mongo db 客户端执行事务 并在遇到可重试错误时重试该事务 这是该函数的最小可重现示例 use mongodb Client Co