首页 > 解决方案 > 我可以将值借入闭包而不是移动它们吗?

问题描述

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

为了避免一堆嵌套match,我尝试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))
}

标签: rustlmdbactix-web

解决方案


可悲的是,在我的情况下,不可能将值借入关闭,因为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(""))
}

推荐阅读