Ownership/borrowing 尝试从单独的函数获取数据库连接和 tx 时出现问题
Ownership/borrowing issues when trying to get a DB connection and tx from a separate function
我有一个共同的功能,即returns一个MySQL连接:
pub fn get_db_conn() -> Result<PooledConn> {...}
在整个应用程序中,我都有这样的代码:
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
这有点违反了 DRY 原则,我想通过只调用一个 single 函数来简化代码。所以我想像这样:
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))
}
或者这个:
pub fn get_db_tx() -> Result<(Transaction)> {
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
Ok((tx))
}
然而,这些都无法编译。
我们以第一个为例。这是编译器发出的错误:
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>)> {
| ~~~~~~~~~~~~~~~~~~~~
我尝试将结果更改为 Result<(PooledConn, Transaction<'static>)>
。这次我得到了:
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
为什么我会收到该错误消息?我要返回 both conn
和 tx
,所以不应该将这两个值的所有权转移给调用函数吗?
此外,我想知道实现第二个功能的最佳方法是什么 returns 只是一个 Transaction
。
你的代码等同于这个:
fn tuple() -> (i32, &i32) {
let x = 42;
(x, &x)
}
产生相同的错误消息:
1 | fn tuple() -> (i32, &i32) {
| ^ expected named lifetime parameter
这个问题叫做“自引用结构”(在你的例子中是自引用元组,但概念相同),对许多 Rust 用户来说是一个持续的痛苦,因为它没有完全令人满意的解决方案。
有很多第三方 crate 可以解决这个问题。目前我个人最喜欢的是 ouroboros
.
但如果你只是想避免输入两行,你可以使用宏,虽然不漂亮,但它很短(你可以添加你的数据库代码而不是数字):
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);
}
但是对于您的数据库连接和事务的特定情况,我认为不值得,我只会使用两个变量和两个函数调用。
我有一个共同的功能,即returns一个MySQL连接:
pub fn get_db_conn() -> Result<PooledConn> {...}
在整个应用程序中,我都有这样的代码:
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
这有点违反了 DRY 原则,我想通过只调用一个 single 函数来简化代码。所以我想像这样:
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))
}
或者这个:
pub fn get_db_tx() -> Result<(Transaction)> {
let mut conn = get_db_conn()?;
let mut tx = conn.start_transaction(TxOpts::default())?;
Ok((tx))
}
然而,这些都无法编译。
我们以第一个为例。这是编译器发出的错误:
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>)> {
| ~~~~~~~~~~~~~~~~~~~~
我尝试将结果更改为 Result<(PooledConn, Transaction<'static>)>
。这次我得到了:
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
为什么我会收到该错误消息?我要返回 both conn
和 tx
,所以不应该将这两个值的所有权转移给调用函数吗?
此外,我想知道实现第二个功能的最佳方法是什么 returns 只是一个 Transaction
。
你的代码等同于这个:
fn tuple() -> (i32, &i32) {
let x = 42;
(x, &x)
}
产生相同的错误消息:
1 | fn tuple() -> (i32, &i32) {
| ^ expected named lifetime parameter
这个问题叫做“自引用结构”(在你的例子中是自引用元组,但概念相同),对许多 Rust 用户来说是一个持续的痛苦,因为它没有完全令人满意的解决方案。
有很多第三方 crate 可以解决这个问题。目前我个人最喜欢的是 ouroboros
.
但如果你只是想避免输入两行,你可以使用宏,虽然不漂亮,但它很短(你可以添加你的数据库代码而不是数字):
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);
}
但是对于您的数据库连接和事务的特定情况,我认为不值得,我只会使用两个变量和两个函数调用。