【问题标题】:Can I borrow values into a closure instead of moving them?我可以将值借入闭包而不是移动它们吗?
【发布时间】:2020-03-01 11:06:39
【问题描述】:

我正在为actix-web 编写的服务器应用程序编写一个 GET 方法。 LMDB 是我使用的数据库,它的事务需要在其生命周期结束之前中止或提交。

为了避免一堆嵌套的matching,我尝试在所有返回结果的函数上使用map_err。在那里我尝试中止事务,但事务被移入关闭而不是被借用。

有什么办法可以将事务借入闭包中,还是我必须硬着头皮写一堆嵌套匹配?本质上,编写这个函数最符合人体工程学的方式是什么?

示例代码(见txn.abort()旁边的cmets):

pub async fn get_user(db: Data<Database>, id: Identity) -> Result<Json<User>, Error> {
    let username = id.identity().ok_or_else(|| error::ErrorUnauthorized(""))?;

    let txn = db
        .env
        .begin_ro_txn()
        .map_err(|_| error::ErrorInternalServerError(""))?;

    let user_bytes = txn.get(db.handle_users, &username).map_err(|e| {
        txn.abort(); // txn gets moved here

        match e {
            lmdb::Error::NotFound => {
                id.forget();
                error::ErrorUnauthorized("")
            }
            _ => error::ErrorInternalServerError(""),
        }
    })?;

    let user: User = serde_cbor::from_slice(user_bytes).map_err(|_| {
        txn.abort(); // cannot use txn here as is was moved
        error::ErrorInternalServerError("")
    })?;

    txn.abort(); // cannot use txn here as is was moved
    Ok(Json(user))
}

【问题讨论】:

  • 不,你不能这样做,因为abort 消耗事务,所以它必须被移动到闭包中
  • 如果你想改善嵌套匹配的人体工程学,你可以查看像 map_formdo 这样的板条箱(完全披露:我写了 map_for)。

标签: rust lmdb actix-web


【解决方案1】:

遗憾的是,就我而言,不可能将值借入闭包中,因为abort 消耗了交易。 (感谢@vkurchatkin 的解释)

如果有人有兴趣,我已经制定了一个无论问题如何都能让我满意的解决方案。我可以避免嵌套一堆matches。

我将处理事务的所有逻辑移到一个单独的函数中,然后将函数 Result 的评估延迟到运行 txn.abort() 之后(参见 cmets):

pub async fn get_user(db: Data<Database>, id: Identity) -> Result<Json<User>, Error> {
    let username = id.identity().ok_or_else(|| error::ErrorUnauthorized(""))?;

    let txn = db
        .env
        .begin_ro_txn()
        .map_err(|_| error::ErrorInternalServerError(""))?;

    let user = db_get_user(&db, &txn, &id, &username); // Execute separate function but do not evaluate the function Result yet, notice missing question mark operator!
    txn.abort(); // Abort the transaction after running the code. (Doesn't matter if it was successful or not. This consumes the transaction and it cannot be used anymore.)
    Ok(Json(user?)) // Now evaluate the Result using the question mark operator.
}

// New separate function that uses the transaction.
fn db_get_user(
    db: &Database,
    txn: &RoTransaction,
    id: &Identity,
    username: &str,
) -> Result<User, Error> {
    let user_bytes = txn.get(db.handle_users, &username).map_err(|e| match e {
        lmdb::Error::NotFound => {
            id.forget();
            error::ErrorUnauthorized("")
        }
        _ => error::ErrorInternalServerError(""),
    })?;

    serde_cbor::from_slice(user_bytes).map_err(|_| error::ErrorInternalServerError(""))
}

【讨论】:

    猜你喜欢
    • 2023-01-19
    • 2017-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-27
    • 2012-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多