我如何使用具有 Deref 特性的多态性来拥有一个可以由 Transaction 或 Connection 表示的对象?
How can I use polymorphism with the Deref trait to have a a single object that can be represented by either a Transaction or Connection?
我正在使用 rustqlite library for a SQLite database (playground)
use std::ops::Deref;
pub struct Connection {
}
impl Connection {
pub fn transaction(&mut self) -> Transaction {
Transaction::new(self)
}
}
pub struct Transaction<'conn> {
conn: &'conn Connection
}
impl Transaction<'_> {
pub fn new(conn: &mut Connection) -> Transaction {
Transaction{ conn }
}
pub fn commit(mut self) -> Result<(), ()> {
Ok(())
}
}
impl Deref for Transaction<'_> {
type Target = Connection;
#[inline]
fn deref(&self) -> &Connection {
self.conn
}
}
通过此实现,Transaction
对象将取得 Connection
对象的所有权。同时,它还实现了 Deref
特性,因此我们可以像调用 Connection
结构一样调用 Transaction
结构中的所有方法。
实现细节是here
在我的应用程序代码中,我想要一个可以用 Transaction
或 Connection
表示的对象。这是必要的,因为逻辑有一个标志来决定是否使用事务。有一个转换将 Transaction
对象视为 Connection
对象:
let conn = create_connection(); // Connection
let tx = conn.transaction(); // Transaction
let conn: &Transaction = &tx; // cast back to Connection type from the Transaction type
但是,我不知道如何从具有条件的应用程序 POV 安排此代码。这是我的伪代码:
pub fn execute(is_tx: bool) {
// conn will have Connection type
let conn = match is_tx {
true => &create_connection(),
false => {
let x = create_connection().transaction();
let t: &Connection = &x;
t
}
};
// do other things with conn object
}
pub fn create_connection() -> Connection {
Connection{}
}
但是会报错
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:36:21
|
36 | let x = create_connection().transaction();
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
37 | let t: &Connection = &x;
| -- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0597]: `x` does not live long enough
--> src/lib.rs:37:34
|
37 | let t: &Connection = &x;
| ^^ borrowed value does not live long enough
38 | t
| - borrow later used here
39 | }
| - `x` dropped here while still borrowed
我理解这个错误,但我尝试了几个变通方法但没有成功,主要是因为 Transaction
结构取得了 Connection
结构的所有权。我该如何解决这个问题?
免责声明:我还没有尝试过这段代码。但这只是为了让您了解进入的方向。
首先,Rust 中的局部变量(在堆栈上)必须是固定大小的。这是您面临的问题之一。事务和连接的大小不同。所以你不能在栈上实现“多态”,没有一些技巧。
实现此目的的两种方法是枚举类型和装箱(将结构放在堆上,并添加一个 VTable)。
我不会讲拳击,因为它相对简单。
您遇到的第二个问题是 Transaction
的生命周期与 Connection
相关,因此 Transaction
的任何移动都需要您 move
Connection
还有。
enum MyConnection<'a> {
TransactionConnection {
transaction: Transaction<'a>
},
NakedConnection{
connection: Connection
}
}
impl MyConnection<'a> {
fn commit(mut &self) -> Result<()> {
match self {
MyConnection::NakedConnection =>
Ok(()),
MyConnection::TransactionConnection { transaction } =>
transaction.commit()
}
}
}
impl<'a> Deref for MyConnection<'a>
{
type Target = Connection;
#[inline]
fn deref(&self) -> &Connection {
match self {
MyConnection::TransactionConnection { transaction } =>
transaction.conn,
MyConnection::NakedConnection { connection } =>
connection,
}
}
}
这些枚举和 Deref 将允许您持有一个可以访问连接的结构。
这就是您使用上述代码的方式。
pub fn execute(is_tx: bool) {
// conn will have Connection type
let mut conn = create_connection();
let conn = match is_tx {
false => {
MyConnection::NakedConnection { connection: conn }
},
true => {
let trans = conn.transaction();
MyConnection::TransactionConnection {
transaction: trans,
}
}
};
conn.do_stuff();
conn.commit();
}
请注意 create_connection
已移出匹配项。这样连接的范围将始终大于 MyConnection
的范围 'a
。这“解决”了第二个问题。
我正在使用 rustqlite library for a SQLite database (playground)
use std::ops::Deref;
pub struct Connection {
}
impl Connection {
pub fn transaction(&mut self) -> Transaction {
Transaction::new(self)
}
}
pub struct Transaction<'conn> {
conn: &'conn Connection
}
impl Transaction<'_> {
pub fn new(conn: &mut Connection) -> Transaction {
Transaction{ conn }
}
pub fn commit(mut self) -> Result<(), ()> {
Ok(())
}
}
impl Deref for Transaction<'_> {
type Target = Connection;
#[inline]
fn deref(&self) -> &Connection {
self.conn
}
}
通过此实现,Transaction
对象将取得 Connection
对象的所有权。同时,它还实现了 Deref
特性,因此我们可以像调用 Connection
结构一样调用 Transaction
结构中的所有方法。
实现细节是here
在我的应用程序代码中,我想要一个可以用 Transaction
或 Connection
表示的对象。这是必要的,因为逻辑有一个标志来决定是否使用事务。有一个转换将 Transaction
对象视为 Connection
对象:
let conn = create_connection(); // Connection
let tx = conn.transaction(); // Transaction
let conn: &Transaction = &tx; // cast back to Connection type from the Transaction type
但是,我不知道如何从具有条件的应用程序 POV 安排此代码。这是我的伪代码:
pub fn execute(is_tx: bool) {
// conn will have Connection type
let conn = match is_tx {
true => &create_connection(),
false => {
let x = create_connection().transaction();
let t: &Connection = &x;
t
}
};
// do other things with conn object
}
pub fn create_connection() -> Connection {
Connection{}
}
但是会报错
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:36:21
|
36 | let x = create_connection().transaction();
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
37 | let t: &Connection = &x;
| -- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0597]: `x` does not live long enough
--> src/lib.rs:37:34
|
37 | let t: &Connection = &x;
| ^^ borrowed value does not live long enough
38 | t
| - borrow later used here
39 | }
| - `x` dropped here while still borrowed
我理解这个错误,但我尝试了几个变通方法但没有成功,主要是因为 Transaction
结构取得了 Connection
结构的所有权。我该如何解决这个问题?
免责声明:我还没有尝试过这段代码。但这只是为了让您了解进入的方向。
首先,Rust 中的局部变量(在堆栈上)必须是固定大小的。这是您面临的问题之一。事务和连接的大小不同。所以你不能在栈上实现“多态”,没有一些技巧。
实现此目的的两种方法是枚举类型和装箱(将结构放在堆上,并添加一个 VTable)。
我不会讲拳击,因为它相对简单。
您遇到的第二个问题是 Transaction
的生命周期与 Connection
相关,因此 Transaction
的任何移动都需要您 move
Connection
还有。
enum MyConnection<'a> {
TransactionConnection {
transaction: Transaction<'a>
},
NakedConnection{
connection: Connection
}
}
impl MyConnection<'a> {
fn commit(mut &self) -> Result<()> {
match self {
MyConnection::NakedConnection =>
Ok(()),
MyConnection::TransactionConnection { transaction } =>
transaction.commit()
}
}
}
impl<'a> Deref for MyConnection<'a>
{
type Target = Connection;
#[inline]
fn deref(&self) -> &Connection {
match self {
MyConnection::TransactionConnection { transaction } =>
transaction.conn,
MyConnection::NakedConnection { connection } =>
connection,
}
}
}
这些枚举和 Deref 将允许您持有一个可以访问连接的结构。
这就是您使用上述代码的方式。
pub fn execute(is_tx: bool) {
// conn will have Connection type
let mut conn = create_connection();
let conn = match is_tx {
false => {
MyConnection::NakedConnection { connection: conn }
},
true => {
let trans = conn.transaction();
MyConnection::TransactionConnection {
transaction: trans,
}
}
};
conn.do_stuff();
conn.commit();
}
请注意 create_connection
已移出匹配项。这样连接的范围将始终大于 MyConnection
的范围 'a
。这“解决”了第二个问题。