I wanted to design a "database" interface that encapsulates tables. One can work with tables directly, so the table object needs to reference the connection pool owned by the database object. Here's a sketch of what I mean:
struct Db<'a> {
pool: Pool,
tables: HashMap<String, Table<'a>>,
}
struct Table<'a> {
name: String,
pool: &'a Pool,
}
impl<'a> Db<'a> {
fn new(dsn: &str, tables: &[&str]) -> Self {
let pool = Pool::new(isn);
Db {
pool: pool,
tables: tables
.iter()
.map(|t| {
(
t.to_string(),
Table {
name: t.to_string(),
pool: &pool,
},
)
})
.collect(),
}
}
fn table(&self, table: &str) -> &Table {
self.tables.get(table).unwrap()
}
}
This of course fails because the compiler doesn't know that pool
will be referenced by the the same Db
object as the tables
field is a part of. How can I convince the compiler that the references to pool
in the tables
HashMap will last just as long as the pool itself?
FWIW, I tried adding a method to Db that operates on a mutable &self
with a mutable hash map, like so:
impl<'a> Db<'a> {
fn new(dsn: &str, tables: &[&str]) -> Self {
let mut ret = Db {
pool: Pool::new(dsn),
tables: HashMap::new(),
};
ret.mk_tables(tables);
ret
}
fn mk_tables(&mut self, tables: &[&str]) {
for t in tables {
self.tables.insert(
t.to_string(),
Table {
name: t.to_string(),
pool: &self.pool,
},
);
}
}
fn table(&self, table: &str) -> &Table {
self.tables.get(table).unwrap()
}
}
I figured this way the compiler would know they were part of the same object, but apparently not:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:40:27
|
40 | pool: &self.pool,
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 34:5...
--> src/main.rs:34:5
|
34 | fn mk_tables(&mut self, tables: &[&str]) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:40:27
|
40 | pool: &self.pool,
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 16:6...
--> src/main.rs:16:6
|
16 | impl<'a> Db<'a> {
| ^^
note: ...so that the expression is assignable
--> src/main.rs:38:17
|
38 | / Table {
39 | | name: t.to_string(),
40 | | pool: &self.pool,
41 | | },
| |_________________^
= note: expected `Table<'a>`
found `Table<'_>`
I get that the pool outlives the implicit lifetime of the mut &self
in the method signature, but I'm at a loss to determine how to work around it. Somehow reference the 'a
from the implementation declaration?