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?