Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working with enums

This guide will show you how to use enumerations in the Google Cloud client libraries for Rust, including working with enumeration values introduced after the library was released.

Background

Google Cloud services use enumerations for fields that only accept or provide a discrete and limited set of values. While at any point in time the set of allowed values is known, this list may change over time.

The client libraries are prepared to receive and send enumerations, and support working with values introduced after the library release.

Prerequisites

This guide does not make any calls to Google Cloud services. You can run the examples without having a project or account in Google Cloud. The guide will use the client library for Secret Manager. The same principles apply to any other enumeration in any other client library.

Dependencies

As it is usual with Rust, you must declare the dependency in your Cargo.toml file. We use:

cargo add google-cloud-secretmanager-v1 serde_json

Handling known values

When using known values, you can use the enumeration as usual:

        use google_cloud_secretmanager_v1::model::secret_version::State;
        let enabled = State::Enabled;
        println!("State::Enabled = {enabled}");
        assert_eq!(enabled.value(), Some(1));
        assert_eq!(enabled.name(), Some("ENABLED"));

        let state = State::from(1);
        println!("state = {state}");
        assert_eq!(state.value(), Some(1));
        assert_eq!(state.name(), Some("ENABLED"));

        let state = State::from("ENABLED");
        println!("state = {state}");
        assert_eq!(state.value(), Some(1));
        assert_eq!(state.name(), Some("ENABLED"));
        println!("json = {}", serde_json::to_value(&state)?);

Handling unknown values

When using unknown string values, the .value() function return None, but everything else works as normal:

        use google_cloud_secretmanager_v1::model::secret_version::State;
        use serde_json::json;
        let state = State::from("STATE_NAME_FROM_THE_FUTURE");
        println!("state = {state}");
        assert_eq!(state.value(), None);
        assert_eq!(state.name(), Some("STATE_NAME_FROM_THE_FUTURE"));
        println!("json = {}", serde_json::to_value(&state)?);
        let u = serde_json::from_value::<State>(json!("STATE_NAME_FROM_THE_FUTURE"))?;
        assert_eq!(state, u);

The same principle applies to unknown integer values:

        use google_cloud_secretmanager_v1::model::secret_version::State;
        use serde_json::json;
        let state = State::from("STATE_NAME_FROM_THE_FUTURE");
        println!("state = {state}");
        assert_eq!(state.value(), None);
        assert_eq!(state.name(), Some("STATE_NAME_FROM_THE_FUTURE"));
        println!("json = {}", serde_json::to_value(&state)?);
        let u = serde_json::from_value::<State>(json!("STATE_NAME_FROM_THE_FUTURE"))?;
        assert_eq!(state, u);

Preparing for upgrades

As mentioned above, the Rust enumerations in the client libraries may gain new variants in future releases. To avoid breaking applications we mark these enumerations as #[non_exhaustive].

If you use a match expression for non-exhaustive enumerations then you must include the wildcard pattern in your match. This will prevent compilation problems when new variants are included in the enumeration.

    use google_cloud_secretmanager_v1::model::secret_version::State;
    fn match_with_wildcard(state: State) -> anyhow::Result<()> {
        use anyhow::Error;
        match state {
            State::Unspecified => {
                return Err(Error::msg("the documentation says this is never used"));
            }
            State::Enabled => println!("the secret is enabled and can be accessed"),
            State::Disabled => {
                println!("the secret version is not accessible until it is enabled")
            }
            State::Destroyed => {
                println!("the secret is destroyed, the data is no longer accessible")
            }
            State::UnknownValue(u) => {
                println!("unknown State variant ({u:?}) time to update the library")
            }
            _ => return Err(Error::msg("unexpected value, update this code")),
        };
        Ok(())
    }

Nevertheless, you may want a warning or error if new variants appear, at least so you can examine the code and decide if it must be updated. If that is the case, consider using the wildcard_enum_match_arm clippy warning:

    use google_cloud_secretmanager_v1::model::secret_version::State;
    fn match_with_warnings(state: State) -> anyhow::Result<()> {
        use anyhow::Error;
        #[warn(clippy::wildcard_enum_match_arm)]
        match state {
            State::Unspecified => {
                return Err(Error::msg("the documentation says this is never used"));
            }
            State::Enabled => println!("the secret is enabled and can be accessed"),
            State::Disabled => {
                println!("the secret version is not accessible until it is enabled")
            }
            State::Destroyed => {
                println!("the secret is destroyed, the data is no longer accessible")
            }
            State::UnknownValue(u) => {
                println!("unknown State variant ({u:?}) time to update the library")
            }
            _ => {
                // *If* your CI includes treating clippy warnings as errors,
                // consider using `unreachable!()`.
                return Err(Error::msg("unexpected value, update this code"));
            }
        };
        Ok(())
    }

You may also consider the (currently unstable) non_exhaustive_omitted_patterns lint.