Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

[libraries/pod]: add PodOption type #6886

Merged
merged 8 commits into from
Jul 12, 2024
Merged

Conversation

febo
Copy link
Contributor

@febo febo commented Jun 19, 2024

Problem

Optional values usually require an extra byte to indicate whether the value is present or not. This can affect the alignment of a type, which is not ideal.

Solution

This PR adds a PodOption, which is a generic type that can be used as a Pod to wrap a type as a Option. It works over types that implement a Nullable trait in order to indicate whether its value represent a None or not.

For example, a PodOption<u8> assumes the a 0 value is a None and any value different than 0 is Some.

/// Trait for types that can be `None`.
///
/// This trait is used to indicate that a type can be `None` according to a
/// specific value.
pub trait Nullable: Pod {
    /// Indicates whether the value is `None` or not.
    fn is_none(&self) -> bool;
}

impl Nullable for u8 {
    fn is_none(&self) -> bool {
        *self == 0
    }
}

The PR includes implementation for all integer types and Pubkey.

@febo febo requested review from joncinque and buffalojoec June 19, 2024 23:28
@febo febo changed the title Add pod option type [libraries/pod]: add PodOption type Jun 20, 2024
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really good! Just a few small things on my side

libraries/pod/src/lib.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API itself is clean, but I'm generally concerned with defaulting to zero values as None for integers.

I wonder if instead we should architect this type around "nonzero" integers, so the distinction is clear?

https://docs.rs/bytemuck/latest/bytemuck/trait.ZeroableInOption.html

libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
libraries/pod/src/pod_option.rs Outdated Show resolved Hide resolved
@febo febo marked this pull request as ready for review June 24, 2024 20:48
@febo febo requested review from buffalojoec and joncinque June 25, 2024 15:50
Copy link
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is on the right track, @febo! I've just added some suggestions on the trait implementations.

I still think you can add to this type's API to make its use a little bit more ergonomic. I still believe it should at least offer is_some() and is_none() directly on the type.

Up to you, but I think COption does a great job of providing an API similar to Rust's Option, which makes it feel more comfortable to use.

https://github.com/anza-xyz/agave/blob/master/sdk/program/src/program_option.rs

libraries/pod/src/option.rs Outdated Show resolved Hide resolved
libraries/pod/src/option.rs Outdated Show resolved Hide resolved
libraries/pod/src/option.rs Outdated Show resolved Hide resolved
libraries/pod/src/option.rs Outdated Show resolved Hide resolved
libraries/pod/src/option.rs Show resolved Hide resolved
@github-actions github-actions bot added the stale [bot only] Added to stale content; will be closed soon label Jul 10, 2024
@febo
Copy link
Contributor Author

febo commented Jul 10, 2024

This is on the right track, @febo! I've just added some suggestions on the trait implementations.

I still think you can add to this type's API to make its use a little bit more ergonomic. I still believe it should at least offer is_some() and is_none() directly on the type.

Up to you, but I think COption does a great job of providing an API similar to Rust's Option, which makes it feel more comfortable to use.

https://github.com/anza-xyz/agave/blob/master/sdk/program/src/program_option.rs

@buffalojoec I cleaned up the API based on your review, let me know what you think.

@github-actions github-actions bot removed the stale [bot only] Added to stale content; will be closed soon label Jul 11, 2024
@febo febo requested a review from buffalojoec July 12, 2024 08:54
Copy link
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Ship it!

@febo febo merged commit cbd138f into solana-labs:master Jul 12, 2024
8 checks passed
@febo febo deleted the pod-option branch July 12, 2024 22:11
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few flyby comments that I hope will remove some footguns

libraries/pod/src/option.rs Show resolved Hide resolved
libraries/pod/src/option.rs Show resolved Hide resolved
libraries/pod/src/option.rs Show resolved Hide resolved
fn from(from: Option<T>) -> Self {
match from {
Some(value) => PodOption(value),
None => PodOption(T::default()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that T::default() is the None value, which is not guaranteed by the trait. I understand the desire to make the value flexible, so to make that behavior less error-prone, how about changing the trait to something like:

pub trait Nullable: Pod + PartialEq + Sized {
    /// Value indicating the `None` to check against
    const NONE: Self; 
    /// Indicates whether the value is `None` or not.
    fn is_none(&self) -> bool {
        self == &Self::NONE
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only caveat is that in this case NONE value has to be a const. I don't think is a big deal, but perhaps we can consider adding a method to the trait to specify the NONE value.

  • Using const NONE:
    impl Nullable for Pubkey {
        const NONE: Self = Pubkey::new_from_array([0u8; PUBKEY_BYTES]);
    }
  • Using none() method:
    impl Nullable for Pubkey {
        fn none() -> Self {
            Pubkey::default()
        }
    }

joncinque added a commit to joncinque/solana-program-library that referenced this pull request Aug 27, 2024
#### Problem

The publish step is great, but there's still some manual work to add the
changelog and correct title for the created release.

#### Summary of changes

Use git-cliff to generate the release notes. This comes with a very
simple cliff.toml which will do minimal processing, since we don't
follow conventional commits in the repo.

Here's a test run for the given input:

```
$ git-cliff -c ci/cliff.toml "pod-v0.3.0"..master --include-path "libraries/pod/**" --github-repo "solana-labs/solana-program-library"
```

Output:

```
## What's new

- Publish pod v0.3.2 by @github-actions[bot]
- [token-2022] Upgrade to `zk-sdk` (solana-labs#7148) by @samkim-crypto
- Implement `Default` for `PodOption` (solana-labs#7083) by @joncinque
- Bump to 0.3.1 (solana-labs#7075) by @febo
- Improve `PodOption` type (solana-labs#7076) by @febo
- Add `PodU128` type (solana-labs#7012) by @febo
- Add `PodOption` type (solana-labs#6886) by @febo
- Use `bytemuck_derive` explicitly (solana-labs#6928) by @joncinque
```
joncinque added a commit that referenced this pull request Aug 28, 2024
* CI: Add git-cliff step during publish

#### Problem

The publish step is great, but there's still some manual work to add the
changelog and correct title for the created release.

#### Summary of changes

Use git-cliff to generate the release notes. This comes with a very
simple cliff.toml which will do minimal processing, since we don't
follow conventional commits in the repo.

Here's a test run for the given input:

```
$ git-cliff -c ci/cliff.toml "pod-v0.3.0"..master --include-path "libraries/pod/**" --github-repo "solana-labs/solana-program-library"
```

Output:

```
## What's new

- Publish pod v0.3.2 by @github-actions[bot]
- [token-2022] Upgrade to `zk-sdk` (#7148) by @samkim-crypto
- Implement `Default` for `PodOption` (#7083) by @joncinque
- Bump to 0.3.1 (#7075) by @febo
- Improve `PodOption` type (#7076) by @febo
- Add `PodU128` type (#7012) by @febo
- Add `PodOption` type (#6886) by @febo
- Use `bytemuck_derive` explicitly (#6928) by @joncinque
```

* Don't always run the step

* Make the YAML valid

* Trim each commit line to just the first line
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants