Rust:预期类型,发现不透明类型

Rust: expected type, found opaque type

我想在actix中做一个渲染模板的辅助函数,如下所示:

fn render_response(
    tera: web::Data<Tera>,
    template_name: &str,
    context: &Context,
) -> impl Responder {
    match tera.render(template_name, context) {
        Ok(rendered) => HttpResponse::Ok().body(rendered),
        Err(_) => HttpResponse::InternalServerError().body("Failed to resolve the template."),
    }
}

我在下面这样的视图中使用它:

async fn signup(tera: web::Data<Tera>) -> impl Responder {
    let mut data = Context::new();
    data.insert("title", "Sign Up");

    render_response(tera, "signup.html", &data)
}

如果视图像上面那样简单,一切正常,但是如果视图稍微复杂一点,我就会遇到问题:

async fn login(tera: web::Data<Tera>, id: Identity) -> impl Responder {
    let mut data = Context::new();
    data.insert("title", "Login");

    if let Some(id) = id.identity() {
        return HttpResponse::Ok().body(format!("Already logged in: {}.", id));
    }

    render_response(tera, "login.html", &data)
}

我得到的错误:

error[E0308]: mismatched types
   --> src\main.rs:101:5
    |
42  | ) -> impl Responder {
    |      -------------- the found opaque type
...
101 |     render_response(tera, "login.html", &data)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `actix_web::HttpResponse`, found opaque type
    |
    = note:     expected type `actix_web::HttpResponse`
            found opaque type `impl actix_web::Responder`

我试图将 return HttpResponse... 提取到一个单独的函数中,该函数也 returns impl Responder,但我现在遇到了不同的错误:

error[E0308]: mismatched types
   --> src\main.rs:101:5
    |
42  | ) -> impl Responder {
    |      -------------- the found opaque type
...
101 |     render_response(tera, "login.html", &data)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
    |
    = note:     expected type `impl actix_web::Responder` (opaque type at <src\main.rs:104:28>)
            found opaque type `impl actix_web::Responder` (opaque type at <src\main.rs:42:6>)
    = note: distinct uses of `impl Trait` result in different opaque types

我不太明白为什么会发生这种情况以及如何解决它。

当一个函数 return 类似于 impl Responder 时,这意味着它 return 是 某些 类型实现 Responder.编译器可以推断出类型,但它对任何调用者来说都是“不透明的”,这意味着您不能对实际类型做出任何假设。即使编译器有更多的信息,类型总是在函数边界匹配。这很重要,这样人们就可以在函数级别推理代码,而不必在头脑中保留有关程序各行的所有信息。

一个函数也只能return一种类型,所以当编译器遇到这样的函数时:

async fn login(tera: web::Data<Tera>, id: Identity) -> impl Responder {
    let mut data = Context::new();
    data.insert("title", "Login");

    if let Some(id) = id.identity() {
        return HttpResponse::Ok().body(format!("Already logged in: {}.", id));
    }

    render_response(tera, "login.html", &data)
}

它看到最后的陈述 return 是一个不透明的 impl Responder 和早期的 return 陈述 return 是一个具体的 HttpResponse。即使您知道它们实际上是同一类型,那只是因为您碰巧知道 render_response 是如何实现的。这很好,因为它可以防止更改代码的不相关部分破坏此功能:您不希望更改 render_responsebody 导致 类型 login 不匹配。

render_response的return类型改为具体类型即可:

fn render_response(
    tera: web::Data<Tera>,
    template_name: &str,
    context: &Context,
) -> HttpResponse;