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.