Могу ли я заимствовать значения в замыкание вместо их перемещения? - PullRequest
1 голос
/ 28 февраля 2020

Я пишу метод GET для серверного приложения, написанный на actix-web . LMDB - это база данных, которую я использую, ее транзакции должны быть прерваны или зафиксированы до конца их срока действия.

Чтобы избежать множества вложенных match ing, я попытался использовать map_err на всех функциях, которые возвращают результат. Там я пытаюсь прервать транзакцию, но вместо заимствования транзакция перемещается в закрытие.

Есть ли способ вместо того, чтобы заимствовать транзакцию в закрытии, или мне нужно прикусить пулю и написать куча вложенных спичек? По сути, какой самый эргономичный c способ написания этой функции?

Пример кода (см. Комментарии рядом с 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))
}

1 Ответ

1 голос
/ 01 марта 2020

К сожалению, в моем случае невозможно заимствовать значение в замыкании, потому что abort потребляет транзакцию. (Спасибо @vkurchatkin за объяснение этого)

На случай, если кому-то будет интересно, я разработал решение, которое удовлетворит меня независимо от проблемы. Мне удалось избежать вложения группы match es.

. Я переместил все логи c, работающие с транзакцией, в отдельную функцию, а затем отложил оценку функции * 1007. * до запуска 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(""))
}
...