I'm working with a library that offers a RAII transaction API shaped like this:
struct Dataset {}
struct Transaction<'a> {
dataset: &'a mut Dataset,
}
struct Error;
impl Dataset {
fn start_transaction(&mut self) -> Result<Transaction<'_>, Error> {
Ok(Transaction { dataset: self }) // In real life, may also return Err.
}
}
Now I want to write some wrapper code that tries to start a Transaction
on a dataset, but if that fails because the underlying data source does not support transactions, it returns the Dataset
as-is. (Then everything is eventually resolved to a Dataset
through DerefMut
implementations, but that's beside the point.)
Here's my attempt:
enum TransactionIfSupported<'a> {
Transaction(Transaction<'a>),
Dataset(&'a mut Dataset),
}
fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
if let Ok(txn) = dataset.start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(dataset)
}
Sadly the borrow checker frowns upon it:
error[E0499]: cannot borrow `*dataset` as mutable more than once at a time
--> src/lib.rs:28:37
|
24 | fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
| -- lifetime `'a` defined here
25 | if let Ok(txn) = dataset.start_transaction() {
| --------------------------- first mutable borrow occurs here
26 | return TransactionIfSupported::Transaction(txn);
| ---------------------------------------- returning this value requires that `*dataset` is borrowed for `'a`
27 | }
28 | TransactionIfSupported::Dataset(dataset)
| ^^^^^^^ second mutable borrow occurs here
I don't quite understand why this happens. If start_transaction
returns the Err
variant, I'd expect any borrows from the start_transaction
call to no longer be in scope after the if
block. I'd expect this to hold even if there was no unconditional return
statement inside the if
.
What's going on and how do I resolve it?