Reputation: 11758
I have a common function that returns a MySQL connection:
pub fn get_db_conn() -> Result<PooledConn> {...}
And all around the application I have code like this:
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
That kinda breaks the DRY principle and I'd like to simplify that code by only having to call a single function. So I image something like this:
pub fn get_db_conn_and_tx() -> Result<(PooledConn, Transaction)> {
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
Ok((conn, tx))
}
or this:
pub fn get_db_tx() -> Result<(Transaction)> {
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
Ok((tx))
}
However, neither of those compiles.
Let's take the first one as an example. This is the error the compiler emits:
error[E0106]: missing lifetime specifier
|
33 | pub fn get_db_conn_with_tx() -> Result<(PooledConn, Transaction)> {
| ^^^^^^^^^^^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
|
33 | pub fn get_db_conn_with_tx() -> Result<(PooledConn, Transaction<'static>)> {
| ~~~~~~~~~~~~~~~~~~~~
I tried changing the result to Result<(PooledConn, Transaction<'static>)>
. This time I got:
error[E0515]: cannot return value referencing local variable `conn`
|
35 | let mut tx = conn.start_transaction(TxOpts::default())?;
| ---- `conn` is borrowed here
36 | Ok((conn, tx))
| ^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
Why do I get that error message? I am returning both conn
and tx
, so shouldn't ownership of both values be transferred to the calling function?
Also, I wonder what would be the best way to implement the second function that returns just a Transaction
.
Upvotes: 1
Views: 64
Reputation: 98436
Your code is equivalent to this one:
fn tuple() -> (i32, &i32) {
let x = 42;
(x, &x)
}
That produces the same error message:
1 | fn tuple() -> (i32, &i32) {
| ^ expected named lifetime parameter
This issue is called "self referential structs" (self referential tuple in your case, but the same concept), and is a persistent pain for many Rust users, because it does not have a full satisfactory solution.
There a bunch of third party crates that tackle this problem. My personal favorite is currently ouroboros
.
But if you just want to avoid typing two lines you can use a macro, not pretty, but it is short (you can add your database code instead of numbers):
macro_rules! self_ref {
($r:ident) => {
let $r = 42;
//Shadow the first variable, it is borrowed anyway
let $r = &$r;
}
}
fn main() {
self_ref!(r);
dbg!(r);
}
But for your particular case of a DB connection and a transaction, I don't think it is worth it, I would just use two variables and two function calls.
Upvotes: 1