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.