Repository: bevyengine/bevy
File: crates/bevy_math/src/common_traits.rs (Rust)

Code before:
macro_rules! impl_stable_interpolate_tuple {
    ($(#[$meta:meta])* $(($n:tt, $T:ident)),*) => {
        $(#[$meta])*
        impl<$($T: StableInterpolate),*> StableInterpolate for ($($T,)*) {
            fn interpolate_stable(&self, other: &Self, t: f32) -> Self {
                (
                    $(
                        <$T as StableInterpolate>::interpolate_stable(&self.$n, &other.$n, t),
                    )*
                )
            }
        }
    };
}

all_tuples_enumerated!(
    #[doc(fake_variadic)]
    impl_stable_interpolate_tuple,
    1,
    11,
    T
);

/// Error produced when the values to be interpolated are not in the same units.
#[derive(Clone, Debug, Error, Display)]
pub struct MismatchedUnitsError;

/// A trait that indicates that a value _may_ be interpolable via [`StableInterpolate`]. An
/// interpolation may fail if the values have different units - for example, attempting to
/// interpolate between [`Val::Px`] and [`Val::Percent`] will fail,
/// even though they are the same Rust type.
///
/// The motivating case for this trait is animating UI entities with [`Val`]
/// properties, which, because they are enums, cannot be interpolated in the normal way. This same
/// concept can be extended to other types as well.
///
/// Fallible interpolation can be used for animated transitions, which can be set up to fail
/// gracefully if the the values cannot be interpolated. For example, the a transition could smoothly
/// go from `Val::Px(10)` to `Val::Px(20)`, but if the user attempts to go from `Val::Px(10)` to
/// `Val::Percent(10)`, the animation player can detect the failure and simply snap to the new
/// value without interpolating.
///
/// An animation clip system can incorporate fallible interpolation to support a broad set of
/// sequenced parameter values. This can include numeric types, which always interpolate,
/// enum types, which may or may not interpolate depending on the units, and non-interpolable
/// types, which always jump immediately to the new value without interpolation. This meaas, for
/// example, that you can have an animation track whose value type is a boolean or a string.
///
/// Interpolation for simple number and coordinate types will always succeed, as will any type
/// that implements [`StableInterpolate`]. Types which have different variants such as
/// [`Val`] and [`Color`] will only fail if the units are different.

Review comment (bug):
The error should have a descriptive string for its Display impl.

Also, turns out `thiserror::Error` lets us skip needing to derive `Display` separately, by using `#[error("...")]`?

```suggestion
#[derive(Clone, Debug, Error)]
#[error("cannot interpolate between two values of different units")]
pub struct MismatchedUnitsError;
```

Code after:
    ($(#[$meta:meta])* $(($n:tt, $T:ident)),*) => {
        $(#[$meta])*
        impl<$($T: StableInterpolate),*> StableInterpolate for ($($T,)*) {
            fn interpolate_stable(&self, other: &Self, t: f32) -> Self {
                (
                    $(
                        <$T as StableInterpolate>::interpolate_stable(&self.$n, &other.$n, t),
                    )*
                )
            }
        }
    };
}

all_tuples_enumerated!(
    #[doc(fake_variadic)]
    impl_stable_interpolate_tuple,
    1,
    11,
    T
);

/// Error produced when the values to be interpolated are not in the same units.
#[derive(Clone, Debug, Error)]
#[error("cannot interpolate between two values of different units")]
pub struct MismatchedUnitsError;

/// A trait that indicates that a value _may_ be interpolable via [`StableInterpolate`]. An
/// interpolation may fail if the values have different units - for example, attempting to
/// interpolate between [`Val::Px`] and [`Val::Percent`] will fail,
/// even though they are the same Rust type.
///
/// Fallible interpolation can be used for animated transitions, which can be set up to fail
/// gracefully if the the values cannot be interpolated. For example, the a transition could smoothly
/// go from `Val::Px(10)` to `Val::Px(20)`, but if the user attempts to go from `Val::Px(10)` to
/// `Val::Percent(10)`, the animation player can detect the failure and simply snap to the new
/// value without interpolating.
///
/// An animation clip system can incorporate fallible interpolation to support a broad set of
/// sequenced parameter values. This can include numeric types, which always interpolate,
/// enum types, which may or may not interpolate depending on the units, and non-interpolable
/// types, which always jump immediately to the new value without interpolation. This meaas, for
/// example, that you can have an animation track whose value type is a boolean or a string.
///
/// Interpolation for simple number and coordinate types will always succeed, as will any type
/// that implements [`StableInterpolate`]. Types which have different variants such as
/// [`Val`] and [`Color`] will only fail if the units are different.
/// Note that [`Color`] has its own, non-fallible mixing methods, but those entail
/// automatically converting between different color spaces, and is both expensive and complex.
/// [`TryStableInterpolate`] is more conservative, and doesn't automatically convert between
/// color spaces. This produces a color interpolation that has more predictable performance.