rustactix-webtera

How to convert tera::Error to actix_web::Error?


I'm studying rust/actix/tera and cannot figure out how to implement ResponseError trait on the tera::Error, or alternatively how to convert tera::Error to actix_web::Error.

With the following code snippet:

match TEMPLATES.render("index.html", &ctx) {
    Ok(s) => Ok(HttpResponse::Ok().body(s)),
    Err(e) => Err(e),
}

I'm getting an error:

mismatched types

expected struct `actix_web::Error`, found struct `tera::Error`rustc(E0308)
main.rs(71, 23): expected struct `actix_web::Error`, found struct `tera::Error`

So I tried the following:

match TEMPLATES.render("index.html", &ctx) {
    Ok(s) => Ok(HttpResponse::Ok().body(s)),
    Err(e) => Err(e.into()),
}

But in this case I get:

the trait bound `tera::Error: actix_web::ResponseError` is not satisfied

the trait `actix_web::ResponseError` is not implemented for `tera::Error`

note: required because of the requirements on the impl of `std::convert::From<tera::Error>` for `actix_web::Error`
note: required because of the requirements on the impl of `std::convert::Into<actix_web::Error>` for `tera::Error`rustc(E0277)
main.rs(71, 25): the trait `actix_web::ResponseError` is not implemented for `tera::Error`

So finally I've tried:

use actix_web::{get, post, web, error, Error, ResponseError,
    HttpRequest, HttpResponse, HttpServer,
    App, Responder};
use tera::{Tera, Context};
use tera;

impl ResponseError for tera::Error {}

But now getting:

only traits defined in the current crate can be implemented for arbitrary types

impl doesn't use only types from inside the current crate

note: define and implement a trait or new type insteadrustc(E0117)
main.rs(12, 1): impl doesn't use only types from inside the current crate
main.rs(12, 24): `tera::Error` is not defined in the current crate

Function signature in which I have the match block is the following:

#[get("/")]
async fn tst() -> Result<HttpResponse, Error> {}

What am I doing wrong?


Solution

  • As you have already discovered, into() cannot be used to convert the tera::Error to an actix_web::Error because no such conversion is directly defined. In this case it gives you a slightly misleading error - because this conversion exists:

    impl<T> From<T> for Error
    where
        T: 'static + ResponseError, 
    

    The compiler throws an error saying that if only the tera error implemented ResponseError, it could do the conversion. But you cannot make it implement that trait, because of the 'trait orphan rule', which says that you cannot implement a trait from outside your own crate onto a type from outside your own crate.

    You could wrap the tera::Error in your own struct, and then implement ResponseError for that, as outlined in this question.

    But there is a simpler solution: actix-web provides a whole swathe of helper functions for converting errors, which you can use like so:

    match TEMPLATES.render("index.html", &ctx) {
        Ok(s) => Ok(HttpResponse::Ok().body(s)),
        Err(e) => Err(error::ErrorInternalServerError(e)),
    }
    

    The provided helpers map the supplied error into the named HTTP response code, so you can pick one that most closely represents the error that occurred.

    See also the actix-web documentation for error handling.

    Caveat Emptor: this solution is untested and I have never used tera nor actix-web, rather I gleaned this from skimming their documentation.