Help with returning different types from if else

I have an if else condition where it will return a different (askama)template based on the condition. Since the 2 templates are considered different type, I think I have to use an enum to make a generic template type?

The error:

 `if` and `else` have incompatible types
expected type `Result<HtmlTemplate<SigninTemplate>, _>`
   found enum `Result<HtmlTemplate<UserSigninTemplate>, _>`

I can't find how to do it though,

Here is the relevant code so far, but the enum is probably completely wrong and I can'T find the way to call it properly:

pub async fn handle_signin(
    Form(signinuser): Form<SigninUser>,
    app_state: Extension<Arc<AppState>>,
) -> impl IntoResponse {
    let argon2 = Argon2::default();
    let parsed_hash = PasswordHash::new(&signinuser.current_password).unwrap();
    let username = signinuser.current_username.clone();
    let user = get_user_record(&app_state.db, username.to_string()).await;
    

    if let Ok(user) = user {
        let does_password_match = argon2.verify_password(&user.user_password.as_bytes(), 
                                                        &parsed_hash
                                                    );       
                                                    
        if !does_password_match.is_ok() {
            let template = SigninTemplate {   // <<<<<<< enum call should be here??
                error: "Invalid login".to_string(),
                current_name: signinuser.current_username,              
                current_password: signinuser.current_password,
            };
            Ok(HtmlTemplate(template))
        } else {
            let template = UserSigninTemplate {
                name: signinuser.current_username,
                title: "Signin Confirmation!".to_string(),
                message: "You have successfully logged in to Zata!!".to_string(),
            };
            Ok(HtmlTemplate(template))
        }                                                    
    } else {
        let template = SigninTemplate {   //<<<<<<< enum call should be here??
            error: "User not found.  Wrong Username or Register first?".to_string(),
            current_name: signinuser.current_username,              
            current_password: signinuser.current_password,
        };
        Ok(HtmlTemplate(template))
    }
}

#[derive(Template, Debug, Deserialize)]
#[template(path = "signin.html")]
pub struct SigninTemplate {
    pub error: String,
    pub current_name: String,
    pub current_password: String,
}

#[derive(Template, Debug, Deserialize)]
#[template(path = "user.html")]
pub struct UserSigninTemplate {
    pub title: String,
    pub name: String,
    pub message: String,
}

enum ZataTemplates {
   UserSigninTemplate ,
   SigninTemplate,

}
pub struct HtmlTemplate<T>(pub T);

impl<T> IntoResponse for HtmlTemplate<T>
where
    T: Template,
{
    fn into_response(self) -> Response<BoxBody> {
        match self.0.render() {
            Ok(html) => Html(html).into_response(),
            Err(err) => Response::builder()
                .status(StatusCode::INTERNAL_SERVER_ERROR)
                .body(body::boxed(Full::from(format!(
                    "Failed to render template. Error: {}",
                    err
                ))))
                .unwrap(),
        }
    }
}

You are almost there. Rust compiler itself uses something like this.

And if you look on it you would see what's wrong with your enum. It should be:

```
enum ZataTemplates {
   UserSigninTemplate(UserSigninTemplate) ,
   SigninTemplate(SigninTemplate),
}
```

With obvious way to use it. In your enum UserSigninTemplate and SigninTemplate are not structs, but names of enum variants, but you can put your structs inside.

P.S. Of course it may or may not be the right approach depending on what you actually need from these templates. The other solution would be using dyn Trait if you don't care about type of template but only about ability to use it in certain way,

1 Like

After reading your code, I believe you don't need HtmlTemplate wrapper. What you need is a way to convert your Templates to a Response.

impl<T: Template> IntoResponse for T {
    fn into_response;
}

If you don't want it to work with all Templates but the Templates you explicitly allowed, here is one way to achieve it. Disclaimer: I haven't verified it yet!

trait Mine { }
impl Mine for UserSigninTemplate
impl Mine for SigninTemplate

type MyTemplate = Mine + Template;

impl<T: MyTemplate> IntoResponse for T {
    fn into_response;
}

Thanks, I am gonna look into all the potentials solutions listed so far. dyn intrigues me, I admit to being unfamiliar with it(I saw some bits about it a bit in The Book, but never went deeper.)

dyn Trait is how you implement classic Java-style OOP in Rust. With vtables and interfaces.
Like in modern C++ typical modern Rust program doesn't use it often. Note: from C++ POV all Rust programs are modern, obviously.
But it's valid tool and you may find it best for your purposes in some cases.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.