Functions that depends on never type fallback being `()`

Hello there I've a couple of functions that return a anyhow::Result<()> because I don't need the result if it is ok, but I need to know if it failed. However with the current stable version of rust this returns an error:

rustc: this function depends on never type fallback being `()`
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
specify the types explicitly

what should I switch to?

Can you show an example function that triggers this error? You have something whose return type is equivalent to Ok(!) somehow, and where the compiler currently converts this to Ok(()), but will stop doing that in the future.

2 Likes

Sure:

/// user_session is us:{user_id}:{session_id}
    pub async fn send_user_event(
        &self,
        conn: &mut ConnectionManager,
        user_session: &str,
        event_name: &str,
        event_data: Value,
    ) -> Result<()> {
        trace!(?event_name, ?event_data, "Sending event to user");
        let _: () = self
            .send_user_event_without_response_error(conn, user_session, event_name, event_data)
            .await
            .into_server_error()?;

        Ok(())
    }

At a guess, this is caused by into_server_error - without knowing where that's from, I can't be sure that I'm right - and I'd guess that changing that line to .map_err(|e| e.into_server_error())?; will help type inference get it right.

They are just helper functions to make it easy to convert between std Result to axum results.

here is the implementation:


#[allow(unused)]
pub trait AxumResult<T> {
    fn into_server_error(self) -> std::result::Result<T, Response>;
    fn into_failed_dependency(self) -> std::result::Result<T, Response>;
    fn into_not_found(self) -> std::result::Result<T, Response>;
    fn into_bad_request(self) -> std::result::Result<T, Response>;
    fn into_forbidden(self) -> std::result::Result<T, Response>;
    fn into_not_acceptable(self) -> std::result::Result<T, Response>;
    fn into_unauthorized(self) -> std::result::Result<T, Response>;
    fn into_conflict(self) -> std::result::Result<T, Response>;
}

impl<T, E> AxumResult<T> for Result<T, E>
where
    E: std::fmt::Debug + Send + 'static,
{
    fn into_server_error(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                &json!({
                    "err": format!("Internal Server Error: {e:?}")
                }),
            )),
        }
    }

    fn into_failed_dependency(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::FAILED_DEPENDENCY,
                &json!({
                    "err": format!("Failed Dependency Error: {e:?}")
                }),
            )),
        }
    }

    fn into_not_found(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::NOT_FOUND,
                &json!({
                    "err": format!("Not Found Error: {e:?}")
                }),
            )),
        }
    }

    fn into_bad_request(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::BAD_REQUEST,
                &json!({
                    "err": format!("Bad Request Error: {e:?}")
                }),
            )),
        }
    }

    fn into_forbidden(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::FORBIDDEN,
                &json!({
                    "err": format!("Forbidden Error: {e:?}")
                }),
            )),
        }
    }

    fn into_not_acceptable(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::NOT_ACCEPTABLE,
                &json!({
                    "err": format!("Not Acceptable Error: {e:?}")
                }),
            )),
        }
    }

    fn into_unauthorized(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::UNAUTHORIZED,
                &json!({
                    "err": format!("Unauthorized Error: {e:?}")
                }),
            )),
        }
    }

    fn into_conflict(self) -> std::result::Result<T, Response> {
        match self {
            Ok(e) => Ok(e),
            Err(e) => Err(json_response(
                StatusCode::CONFLICT,
                &json!({
                    "err": format!("Conflict Error: {e:?}")
                }),
            )),
        }
    }
}

Where, specifically, does the error message point to in your code? If unclear, then if you comment out the first statement, does the error remain? What about the second?

The let _: () = portion of the second statement is also unusual. What's the purpose of creating an anonymous binding? Given the code you've shown so far, the return type of send_user_event_without_response_error is likely sufficient for Rust to infer the type of the overall statement.

Without it rustc complains about another thing

This is the other function implementation

pub async fn send_user_event_without_response_error(
        &self,
        conn: &mut ConnectionManager,
        user_session: &str,
        event_name: &str,
        event_data: Value,
    ) -> anyhow::Result<()> {
        trace!(?event_name, ?event_data, "Sending event to user");

        let body = serde_json::to_string(&UserEvent {
            event_name: event_name.to_string(),
            data: serde_json::to_string(&event_data)
                .context("Failed to convert event_data to string")?,
        })
        .context("Failed to convert value to string")?;

        let _: () = conn
            .publish(format!("user_events:{user_session}"), body)
            .await?;

        Ok(())
    }

I just noticed, If I change the implementation to:

pub async fn send_user_event(
        &self,
        conn: &mut ConnectionManager,
        user_session: &str,
        event_name: &str,
        event_data: Value,
    ) -> Result<()> {
        trace!(?event_name, ?event_data, "Sending event to user");
        let _: () = self
            .send_user_event_without_response_error(conn, user_session, event_name, event_data)
            .await
            .into_server_error()?;

        Ok(())
    }

aka, adding let _:() =

Warning disappears

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.