我可以将值借用到闭包中而不是移动它们吗?
Can I borrow values into a closure instead of moving them?
我正在为用 actix-web. LMDB 编写的服务器应用程序编写 GET 方法是我使用的数据库,它的事务需要在其生命周期结束前中止或提交。
为了避免一堆嵌套的match
ing,我尝试在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 的解释)
万一有人感兴趣,我已经制定了一个无论问题如何都让我满意的解决方案。我有可能避免嵌套一堆 match
es.
我将处理交易的所有逻辑都移到了一个单独的函数中,然后将函数 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(""))
}
我正在为用 actix-web. LMDB 编写的服务器应用程序编写 GET 方法是我使用的数据库,它的事务需要在其生命周期结束前中止或提交。
为了避免一堆嵌套的match
ing,我尝试在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 的解释)
万一有人感兴趣,我已经制定了一个无论问题如何都让我满意的解决方案。我有可能避免嵌套一堆 match
es.
我将处理交易的所有逻辑都移到了一个单独的函数中,然后将函数 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(""))
}