繁体   English   中英

如何使用具有 Deref 特征的多态性来获得可以由事务或连接表示的单个 object?

[英]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库用于 SQLite 数据库(操场)

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 object 将获得Connection object 的所有权。 同时,它还实现了Deref trait,因此我们可以像从Connection结构中一样调用Transaction结构中的所有方法。

实现细节在这里

从我的应用程序代码中,我想要一个可以由TransactionConnection表示的 object 。 这是必要的,因为逻辑有一个标志来决定是否使用事务。 有一个强制转换将Transaction object 视为Connection object:

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结构的所有权。 我怎样才能解决这个问题?

免责声明:我没有尝试过这段代码。 不过这里只是为了让大家大致了解一下go in的方向。

首先,Rust 中的局部变量(在堆栈上)必须是固定大小的。 这是您面临的问题之一。 Transaction 和 Connection 的大小不同。 因此,如果没有一些技巧,您就无法在堆栈上实现“多态性”。

执行此操作的两种方法是枚举类型和装箱(将结构放在堆上,并添加 VTable)。

我不会 go 超过拳击,因为这相对简单。

您遇到的第二个问题是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已移出匹配项。 这样一来,连接的 scope 将始终大于MyConnection 'a scope'a。 这“解决”了第二个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM