我可以将值借用到闭包中而不是移动它们吗?

Can I borrow values into a closure instead of moving them?

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

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

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

示例代码(参见 txn.abort() 旁边的注释):

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 消耗了事务。 (感谢@vkurchatkin 的解释)

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

我将处理交易的所有逻辑都移到了一个单独的函数中,然后将函数 Result 的计算延迟到 运行 txn.abort() 之后(见评论):

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(""))
}