The examples of this repository do something like connection.prepare("SELECT ...").query(...), in other words they prepare a statement and then immediately execute it.
Correct me if I'm wrong, but an optimized application should at initialization open the database and prepare all the statements that it will need, and then later execute these statements. No statement is prepared after initialization.
However, the fact that Statement has a lifetime parameter makes this very difficult to do. It is for example not possible to store a Connection and a Statement in the same struct.
This is a well-known problem in the Rust ecosystem, and my opinion is that using lifetimes on long-lived objects is almost always "wrong" (in the sense that it leads to unergonomic designs). Lifetimes exist only to represent a temporary access to the inside of an object (typically a container).
I would suggest three possible ways to fix this:
- Have
Statement always hold an Arc<Connection>.
- Make
Statement generic over a pointer to a connection. For example the user could use Statement<&'a Connection> or Statement<Arc<Connection>> at their discretion. Note that this might lead to unsafety because the user could provide a custom malicious implementation of the Deref trait that doesn't always deref to the same connection.
- Have the
Connection hold some kind of Vec<*mut ffi::Statement> (most likely a Slab so that you can insert and remove elements in O(1) time), and turn Statement into some kind of StatementIndex that represents an index in this container.
The examples of this repository do something like
connection.prepare("SELECT ...").query(...), in other words they prepare a statement and then immediately execute it.Correct me if I'm wrong, but an optimized application should at initialization open the database and prepare all the statements that it will need, and then later execute these statements. No statement is prepared after initialization.
However, the fact that
Statementhas a lifetime parameter makes this very difficult to do. It is for example not possible to store aConnectionand aStatementin the same struct.This is a well-known problem in the Rust ecosystem, and my opinion is that using lifetimes on long-lived objects is almost always "wrong" (in the sense that it leads to unergonomic designs). Lifetimes exist only to represent a temporary access to the inside of an object (typically a container).
I would suggest three possible ways to fix this:
Statementalways hold anArc<Connection>.Statementgeneric over a pointer to a connection. For example the user could useStatement<&'a Connection>orStatement<Arc<Connection>>at their discretion. Note that this might lead to unsafety because the user could provide a custom malicious implementation of theDereftrait that doesn't always deref to the same connection.Connectionhold some kind ofVec<*mut ffi::Statement>(most likely aSlabso that you can insert and remove elements inO(1)time), and turnStatementinto some kind ofStatementIndexthat represents an index in this container.