dylanhart / ulid-rs

This is a Rust implementation of the ulid project

Home Page:https://crates.io/crates/ulid

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Broken Equality using NewType Wrappers around Ulid and Derive Equality macros

moises-marquez opened this issue · comments

Not so long ago I found a weird case of broken equality in my wrapper types around Ulid which I can't find an explanation for after looking at the codebase for ulid-rs. I have created an example where I replicate it, hoping it could help to find out what could be wrong:

use std::fmt;
use ulid::Ulid;

fn main() {
    struct MyNewType {
        id: MyOtherNewTypeId,
    }

    #[derive(Debug, Eq, PartialEq, Copy, Clone)]
    struct MyNewTypeId(Ulid);

    impl MyNewTypeId {
        pub fn parse(string: String) -> Result<Self, ()> {
            Ulid::from_string(&string)
                .map(|ulid| Self(ulid))
                .map_err(|_| ())
        }
    }

    impl AsRef<Ulid> for MyNewTypeId {
        fn as_ref(&self) -> &Ulid {
            &self.0
        }
    }

    struct MyOtherNewType {
        pub id: MyOtherNewTypeId,
    }

    #[derive(Debug, Eq, PartialEq, Copy, Clone)]
    struct MyOtherNewTypeId(Ulid);

    impl MyOtherNewTypeId {
        pub fn new() -> Self {
            Self(Ulid::new())
        }

        pub fn parse(string: String) -> Result<Self, ()> {
            Ulid::from_string(&string)
                .map(|ulid| Self(ulid))
                .map_err(|_| ())
        }
    }

    impl AsRef<Ulid> for MyOtherNewTypeId {
        fn as_ref(&self) -> &Ulid {
            &self.0
        }
    }

    #[derive(Debug, Eq, PartialEq)]
    enum MyIdTypesEnum {
        MyNewTypeIdEnumVariant(MyNewTypeId),
        MyOtherNewTypeIdEnumVariant(MyOtherNewTypeId),
    }

    impl From<MyNewTypeId> for MyIdTypesEnum {
        fn from(my_new_type_id: MyNewTypeId) -> Self {
            Self::MyNewTypeIdEnumVariant(my_new_type_id)
        }
    }

    impl From<MyOtherNewTypeId> for MyIdTypesEnum {
        fn from(my_other_new_type_id: MyOtherNewTypeId) -> Self {
            Self::MyOtherNewTypeIdEnumVariant(my_other_new_type_id)
        }
    }

    impl AsRef<Ulid> for MyIdTypesEnum {
        fn as_ref(&self) -> &Ulid {
            match self {
                Self::MyNewTypeIdEnumVariant(my_new_type_id) => my_new_type_id.as_ref(),
                Self::MyOtherNewTypeIdEnumVariant(my_other_new_type_id) => my_other_new_type_id.as_ref(),
            }
        }
    }

    impl fmt::Display for MyIdTypesEnum {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}", self.as_ref())
        }
    }

    enum MyTypesEnum {
        MyNewTypeEnumVariant(MyNewType),
        MyOtherNewTypeEnumVariant(MyOtherNewType),
    }

    impl MyTypesEnum {
        pub fn get_id(&self) -> MyIdTypesEnum {
            match self {
                Self::MyNewTypeEnumVariant(my_new_type) => my_new_type.id.into(),
                Self::MyOtherNewTypeEnumVariant(my_other_new_type) => my_other_new_type.id.into(),
            }
        }
    }

    struct SomeOtherStruct {
        pub new_types_id: MyIdTypesEnum,
    }

    let some_other_struct = SomeOtherStruct {
        new_types_id: MyIdTypesEnum::MyNewTypeIdEnumVariant(
            MyNewTypeId::parse("01FJ4HS4AQCEBWTGM951G1NHE6".to_string()).unwrap(),
        ),
    };
    let my_types_enum = MyTypesEnum::MyOtherNewTypeEnumVariant(MyOtherNewType {
        id: MyOtherNewTypeId::parse("01FJ4HS4AQCEBWTGM951G1NHE6".to_string()).unwrap(),
    });
    println!(
        "Hello, broken equality! Equality is: {:?}",
        &some_other_struct.new_types_id == &my_types_enum.get_id()
        // &some_other_struct.new_types_id.to_string() == &my_types_enum.get_id().to_string()
    );
}

Will also follow up with the community and see what they think about this.

Thanks in advance!

Closing it. The issue was that I was comparing two different MyIdTypesEnum enum variants, and this breaks the equality check.