Use Actix with Rusqlite

Hello, I made a simple learning project, with Actix and rusqlite:
Here's my code:

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use rusqlite::{params, Connection, Result};

#[derive(Debug)]
struct Person {
    id: i32,
    name: String,
    data: Option<Vec<u8>>,
}

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

#[actix_web::main]
async fn main() -> Result<(), rusqlite::Error>{
    let conn = Connection::open_in_memory()?;

    conn.execute(
        "CREATE TABLE person (
            id   INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            data BLOB
        )",
        (), // empty list of parameters.
    )?;
    let me = Person {
        id: 0,
        name: "Steven".to_string(),
        data: None,
    };
    conn.execute(
        "INSERT INTO person (name, data) VALUES (?1, ?2)",
        (&me.name, &me.data),
    )?;

    let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
    let person_iter = stmt.query_map([], |row| {
        Ok(Person {
            id: row.get(0)?,
            name: row.get(1)?,
            data: row.get(2)?,
        })
    })?;

    for person in person_iter {
        println!("Found person {:?}", person.unwrap());
    };
    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(echo)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await;
    Ok(())
}

It gives this error:


error[E0277]: `?` couldn't convert the error to `rusqlite::Error`
  --> src/main.rs:66:31
   |
66 |     .bind(("127.0.0.1", 8080))?
   |                               ^ the trait `std::convert::From<std::io::Error>` is not implemented for `rusqlite::Error`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following other types implement trait `std::convert::From<T>`:
             <rusqlite::Error as std::convert::From<FromSqlError>>
             <rusqlite::Error as std::convert::From<std::ffi::NulError>>
             <rusqlite::Error as std::convert::From<std::str::Utf8Error>>
   = note: required for `Result<(), rusqlite::Error>` to implement `FromResidual<Result<Infallible, std::io::Error>>`

For more information about this error, try `rustc --explain E0277`.

The actix getting started sample uses std::io::Result<()> which doesn't work with rusqlite, so I use Result<(), rusqlite::Error> to fix that, which works without all the Actix stuff, but Actix doesn't work with the rusqlite fix. How do I fix this?

I'd simply use anyhow, which will allow you to use error propagation with any error type that implements std::error::Error (so you can use ?—the error propagation operator—with both rusqlite::Error and std::io::Error):

async fn main() -> Result<(), anyhow::Error> {

If you want to learn more about error propagation in Rust, I'd recommend the Rust Book:

https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html

It works now, but it adds a dependency, I don't want it to look like a JavaScript project.

Instead of anyhow you could use Box<dyn std::error::Error>:

async fn main() -> Result<(), Box<dyn std::error::Error>> {

Or create your own error type wrapper that implements From<rusqlite::Error> and From<std::io::Error> to make error propagation work. Here a minimal example:

#[derive(Debug)]
enum MyError {
    Rusqlite(rusqlite::Error),
    IO(std::io::Error),
}

impl From<rusqlite::Error> for MyError {
    fn from(e: rusqlite::Error) -> Self {
        Self::Rusqlite(e)
    }
}

impl From<std::io::Error> for MyError {
    fn from(e: std::io::Error) -> Self {
        Self::IO(e)
    }
}

async fn main() -> Result<(), MyError> {

The latter can be helpful in the context of actix-web, if you want to return a ResponseError and need to implement it yourself (i.e. to set the right status code based on the error that was created in your endpoint).

1 Like