DennyHiu
DennyHiu

Reputation: 6060

Rust function that can return more than one type?

I'm new to Rust and there is a function in actix-web that intrigue me. To create a router in actix, we need to pass a function (handle_get_insert for example) that implements actix_web::Responder

// main.rs
cfg.service(
    web::resource("/items/get")
        .route(web::get().to(handle_get_item))
        .route(web::head().to(|| HttpResponse::MethodNotAllowed())),

// handle_get_item is a function that returns any type that implement use actix_web::Responder
// Here Responder means any type that it return must have #[derive(Serialize, Deserialize)] 
pub async fn handle_get_item(....) -> impl Responder {
    match reply {
        Ok(item_doc) => {
            let result = GetReply {
                id: String::from(inf.id.to_string()),
                doc: Some(item_doc),
                ok: true,
            };
            // convert the result into json
            return web::Json(result);
        }
        Err(e) => {
            let result = ErrorReply {
                error: format!("{}", e),
                ok: false,
            };
            // error is occurred here: expected struct "GetReply", found struct "ErrorReply"
            return web::Json(result);
        }
    }
}

this handle_get_item can return two type of value (GetReply for data and ErrorReply if an error occurred). Both types have the same traits but with different fields in it.

To my surprise, Rust only recognize one of them as the return type, and throws an error on the ErrorReply:

error: mismatched types
label: expected struct "GetReply", found struct "ErrorReply"

which means that Rust only recognizes GetReply as the returned value and forces each exit point to implements the same GetReply type.

Does it possible to return two types ? or this is not supposed to be happen in actix ?

Upvotes: 1

Views: 1937

Answers (1)

Masklinn
Masklinn

Reputation: 42282

Both types have the same traits but with different fields in it.

Yes but as Ivan C noted that's not what impl Trait does. impl Trait is essentially a placeholder for a normal type which implements the trait (it also makes the return type opaque which lets you e.g. return private types from pub functions).

Does it possible to return two types ?

Technically no, a Rust function can only return values of one type, however there are usually ways around this. One is to return a dynamically dispatched object via Box. This is slightly less efficient, but if the trait is object-safe (note: I've no idea if Responder is) then it lets you leverage trait objects to kinda-sorta return different types from the same function.

An alternative is that the one trait is basically just an intermediate step and ends up converting to a single concrete type at the end. While you'd usually return something implementing the trait for convenience you can… take the final step yourself "by hand".

In Actix, I expect that's HttpResponse, which you can either build "by hand" or using Responder::respond_to.

Upvotes: 2

Related Questions