RFC: Collection Defaults

Status: Implemented

Applies to: client

For a summarized list of proposed changes, see the Changes Checklist section.

This RFC proposes a breaking change to how generated clients automatically provide default values for collections. Currently the SDK generated fields for List generate optional values:

    /// <p> Container for elements related to a particular part.
    pub fn parts(&self) -> Option<&[crate::types::Part]> {
        self.parts.as_deref()
    }

This is almost never what users want and leads to code noise when using collections:

async fn get_builds() {
    let project = codebuild
        .list_builds_for_project()
        .project_name(build_project)
        .send()
        .await?;
    let build_ids = project
        .ids()
        .unwrap_or_default();
    //  ^^^^^^^^^^^^^^^^^^ this is pure noise
}

This RFC proposes unwrapping into default values in our accessor methods.

Terminology

  • Accessor: The Rust SDK defines accessor methods on modeled structures for fields to make them more convenient for users
  • Struct field: The accessors point to concrete fields on the struct itself.

The user experience if this RFC is implemented

In the current version of the SDK, users must call .unwrap_or_default() frequently. Once this RFC is implemented, users will be able to use these accessors directly. In the rare case where users need to distinguish be None and [], we will direct users towards model.<field>.is_some().

async fn get_builds() {
    let project = codebuild
        .list_builds_for_project()
        .project_name(build_project)
        .send()
        .await?;
    let build_ids = project.ids();
    // Goodbye to this line:
    //    .unwrap_or_default();
}

How to actually implement this RFC

In order to implement this feature, we need update the code generate accessors for lists and maps to add .unwrap_or_default(). Because we are returning slices unwrap_or_default() does not produce any additional allocations for empty collection.

Could this be implemented for HashMap?

This works for lists because we are returning a slice (allowing a statically owned &[] to be returned.) If we want to support HashMaps in the future this is possible by using OnceCell to create empty HashMaps for requisite types. This would allow us to return references to those empty maps.

Isn't this handled by the default trait?

No, many existing APIs don't have the default trait.

Changes checklist

Estimated total work: 2 days

  • Update accessor method generation to auto flatten lists
  • Update docs for accessors to guide users to .field.is_some() if they MUST determine if the field was set.