If I have a simple struct, one attribute of which contains a simple enum, how can I best store examples of this struct with their enumerations in the rusqlite database? Something like:
use rusqlite::{params, Connection, Result};
enum Sex{
Unknown,
Male,
Female,
}
struct Person{
name: String,
sex: Sex
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute(
"CREATE TABLE people(
name TEXT NOT NULL,
sex TEXT NOT NULL
)",
(), // empty list of parameters.
)?;
let person_01 = Person{
name: String::from("Adam"),
sex: Sex::Male
};
conn.execute(
"INSERT INTO people (name, sex) VALUES (?1, ?2)",
(&person_01.name, &person_01.sex),
)?;
Ok(())
}
The problem is that sqlite only allows data of restricted types (NULL, INTEGER, REAL, TEXT), trying to use TEXT here for the enum gives the following error:
error[E0277]: the trait bound `Sex: ToSql` is not satisfied
--> src/main.rs:33:9
|
31 | conn.execute(
| ------- required by a bound introduced by this call
32 | "INSERT INTO tasklist (name, sex) VALUES (?1, ?2)",
33 | (&person_01.name, &person_01.sex),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql` is not implemented for `Sex`
This error makes sense, but what is the best way to implement this? Cast the enum to int? I read here that this is "removes the guarantee that values always represent the variants of the enum", which I agree with. It would be nicer to match the string.
I have tried to do this using strum, which allows me to add to_str
& from_str
to the enum, allowing it to be added to the database like so:
#[derive(strum_macros::Display, strum_macros::EnumString, Debug)]
enum Sex{
Unknown,
Male,
Female,
}
...
conn.execute(
"INSERT INTO people (name, sex) VALUES (?1, ?2)",
(&person_01.name, &person_01.sex.to_string())
)?;
and retrieved like so:
let mut stmt = conn.prepare("SELECT name, sex FROM people")?;
let person_itr = stmt.query_map([], |row|{
Ok(
Person{
name: row.get(0)?,
sex: Sex::from_str(String::as_str(&row.get(1)?)).unwrap(),
}
)
});
but this feels messy. Is there a better way?
I have seen here people manually implementing FromSqlRow
for the enum, but is there a better (quicker) way?