Search code examples
rustserver-sent-eventsactix-web

How to fix "cannot infer type of the type parameter E`` declared on the enum `Result`"?


I am writing an online latex editor, the user can edit the latex document in the browser. When user clicks the "compile" button, I want to push the server side latex log output to the client. I choose to use SSE to do this feature. Now I tried to define the SSE server-side API like this:

use actix_web::{middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer};
use futures::StreamExt;

async fn sse_handler(req: HttpRequest, stream: web::Payload) -> HttpResponse {
    let mut res = HttpResponse::Ok()
        .content_type("text/event-stream")
        .streaming(Box::pin(stream.map(|chunk| {
            Ok(actix_web::web::Bytes::from(format!(
                "data: {}\n\n",
                String::from_utf8(chunk.unwrap().to_vec()).unwrap()
            )))
        })));

    if req.headers().get("cache-control").is_none() {
        res.headers_mut().insert(
            actix_web::http::header::CACHE_CONTROL,
            actix_web::http::header::HeaderValue::from_static("no-cache"),
        );
    }

    res
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(Logger::default())
            .route("/events", web::get().to(sse_handler))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

the compiler shows error:

error[E0283]: type annotations needed
   --> src/main.rs:8:13
    |
7   |         .streaming(Box::pin(stream.map(|chunk| {
    |          --------- type must be known at this point
8   |             Ok(actix_web::web::Bytes::from(format!(
    |             ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
    |
    = note: cannot satisfy `_: Into<Box<(dyn std::error::Error + 'static)>>`
note: required by a bound in `HttpResponseBuilder::streaming`
   --> /Users/xiaoqiangjiang/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/actix-web-4.3.1/src/response/builder.rs:324:12
    |
324 |         E: Into<BoxError> + 'static,
    |            ^^^^^^^^^^^^^^ required by this bound in `HttpResponseBuilder::streaming`
help: consider specifying the generic arguments
    |
8   |             Ok::<actix_web::web::Bytes, E>(actix_web::web::Bytes::from(format!(
    |               ++++++++++++++++++++++++++++

I read this error and did not figure out where is going wrong. What should I do to fix this issue and make it work? Also, here is my Cargo.toml:

[package]
name = "rust-learn"
version = "0.1.0"
edition = "2018"

[dependencies]
tokio = { version = "1.17.0", features = ["full"] }
serde = { version = "1.0.64", features = ["derive"] }
serde_json = "1.0.64"
actix-web = "4"
futures = "0.3"

Solution

  • The issue is in this part, where you are returning Ok from a closure:

    .streaming(Box::pin(stream.map(|chunk| {
        Ok(actix_web::web::Bytes::from(format!(
    

    If you look at the signature of that streaming() method, you can see it's generic with respect to the stream's error type:

    fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
        where
            S: Stream<Item = Result<Bytes, E>> + 'static,
            E: Into<BoxError> + 'static,
    

    Any error that satisfies Into<BoxError> + 'static is valid. The issue is the compiler doesn't have enough information to know what error type you want to use. There are several ways you can provide this information. For example, if you want to use std::io::Error:

    .streaming(Box::pin(stream.map(|chunk| {
        std::io::Result::Ok(actix_web::web::Bytes::from(format!(
    

    If you want to use Box<dyn Error>:

    use std::error::Error;
    // ...
    .streaming(Box::pin(stream.map(|chunk| {
        Ok::<_, Box<dyn Error>>(actix_web::web::Bytes::from(format!(
    

    or

    .streaming::<_, Box<dyn Error>>(Box::pin(stream.map(|chunk| {
        Ok(actix_web::web::Bytes::from(format!(