0

I am trying to provide a thin abstraction over a database in Rust. Here's a simplified version of the underlying database API as I understand it:

// Connection to a database
struct Connection {}

impl Connection {
    fn new() -> Connection {
        Connection {}
    }
}

// Prepared SQL statement
struct Statement<'a> {
    conn: &'a Connection,
}

impl Connection {
    // Create a prepared statement
    fn prepare(&self) -> Statement {
        return Statement { conn: self };
    }
}

I want to provide a wrapper which upon construction connects to a database and stores some prepared statements. After constructing the wrapper I don't actually need the raw connection any more.

Here's a simplified version of my code:

struct Wrapper<'a> {
    stmt: Statement<'a>,
}

impl<'a> Wrapper<'a> {
    fn new() -> Wrapper<'a> {
        let conn = Connection::new();
        Wrapper {
            stmt: conn.prepare(),
        }
    }
}

fn main() {
    let _w = Wrapper::new();
}

Wrapper::new fails to compile because the returned value references conn whose lifetime ends at the end of the function ("error[E0515]: cannot return value referencing local variable conn").

The simplest solution is just to require the caller to construct a Connection; but then this is a rather leaky "wrapper" for the database.

Ideally I'd like to move conn out of the function. Since I'm moving the Wrapper out of the function, I thought I could just move conn into the Wrapper:

struct Wrapper<'a> {
    conn: Connection,
    stmt: Statement<'a>,
}

impl<'a> Wrapper<'a> {
    fn new() -> Wrapper<'a> {
        let conn = Connection {};
        Wrapper {
            stmt: conn.prepare(),
            conn,
        }
    }
}

But this just adds another error: "cannot move out of conn because it is borrowed", even if I try to dodge the chicken-and-egg initialisation of the .stmt and .conn fields with Option & mutation hackery. I've read that you can't really have one struct member reference another in rust, which would explain this.

For a while I thought I could use an Rc, but for this to work I think it would need to be on the Statement's reference to the Connection; which is in library code.

Can anyone see which Rust design pattern I'm missing?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Brendan
  • 1,995
  • 1
  • 20
  • 35
  • 1
    *I don't actually need the raw connection any more* — that's not up for you to decide. The library has decided that it is required, which is expressed in the lifetimes and enforced by the compiler. – Shepmaster Nov 25 '19 at 21:46
  • See the "How do I fix it?" section of the [linked duplicate](https://stackoverflow.com/a/32300133/155423). The primary solution is to use the [rental](https://crates.io/crates/rental) crate, which may or may not work for your case. – Shepmaster Nov 25 '19 at 21:48
  • @Shepmaster yes but what I meant was my user code doesn't need a reference - I meant to highlight that I was only trying to keep the `Connection` in the `Wrapper` as a way to control when it gets dropped. – Brendan Nov 28 '19 at 20:17

0 Answers0