Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ToPrimitive and NumCast traits #58

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

nicoburns
Copy link
Contributor

This hooks Au into euclids type casting mechanism and will allow us to convert code like:

let new_viewport_size = Size2D::new(
    Au::from_f32_px(window_size_data.initial_viewport.width),
    Au::from_f32_px(window_size_data.initial_viewport.height),
);

into

let new_viewport_size = window_size_data.initial_viewport.cast::<Au>();


#[inline]
fn to_i8(&self) -> Option<i8> {
None
Copy link
Contributor

Choose a reason for hiding this comment

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

Not much familiar with this, but shouldn't it be self.to_px().to_i8() or such?
Analogous for all returning 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.

None basically means "we don't support this cast", so it seemed like a conservative default for:

  • Unsigned integers. As casting an Au to an unsigned integer is likely a logic error.
  • Signed integers that have a size smaller than the range that Au supports. Again, we probably don't want to do this?

Taking a look at the implementations that num_traits provides for analogous stdlib types, it looks like it does runtime checks to see whether the value is in a range that can be converted.

So we could do that if you think that's better?

Copy link
Contributor

Choose a reason for hiding this comment

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

Even if it may be a weird conversion, if the value can be represented, it seems to make more sense to try to cast. But I will defer to Martin.

Copy link
Member

@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

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

Interesting! I don't have a strong opinion about whether or not we should be conservative or liberal in what kind of casts we allow. Is it possible to make the cast conservative now and then make it more liberal in the future though, by no longer returning None? If that's the case maybe it's better to not do something that is hard to undo and stay conservative for the time being.

src/app_unit.rs Outdated
Comment on lines 340 to 368
#[inline]
fn to_u8(&self) -> Option<u8> {
None
}

#[inline]
fn to_u16(&self) -> Option<u16> {
None
}

#[inline]
fn to_u32(&self) -> Option<u32> {
None
}

#[inline]
fn to_u64(&self) -> Option<u64> {
None
}

#[inline]
fn to_u128(&self) -> Option<u128> {
None
}

#[inline]
fn to_usize(&self) -> Option<usize> {
None
}
Copy link
Member

Choose a reason for hiding this comment

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

It looks like you could simply implement to_u64 and then the other implementations have defaults?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I could do. Did it this way to be explicit. But I can go down that route if you prefer?

@@ -453,3 +539,27 @@ fn serialize() {
let serialized = ron::to_string(&Au(42)).unwrap();
assert_eq!(ron::from_str(&serialized), Ok(Au(42)));
}

#[cfg(feature = "num_traits")]
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be good to expand the tests a bit. There should probably be tests for the unsupported casts for instance.


#[inline]
fn to_i64(&self) -> Option<i64> {
Some(self.to_px() as i64)
Copy link
Member

Choose a reason for hiding this comment

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

I think you can just implement to to_i64() and the others have default implementations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be worth special casing i32, because that's what we actually have? I guess casting to i64 and back again is probably pretty cheap / likely to be inlined. But still...

src/app_unit.rs Outdated
@@ -289,6 +289,92 @@ impl Au {
}
}

#[cfg(feature = "num_traits")]
impl num_traits::ToPrimitive for Au {
// Float conversions
Copy link
Member

Choose a reason for hiding this comment

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

I think these comments aren't adding too much. It's pretty obvious what the conversion is by looking at the type signature.

@Loirooriol
Copy link
Contributor

@emilio had some doubts about this in #46

@nicoburns
Copy link
Contributor Author

Interesting! I don't have a strong opinion about whether or not we should be conservative or liberal in what kind of casts we allow. Is it possible to make the cast conservative now and then make it more liberal in the future though, by no longer returning None? If that's the case maybe it's better to not do something that is hard to undo and stay conservative for the time being.

Yes. I believe so. And this was part of my reasoning for doing it like this.

The effect of doing that is that initially (when None is being called) calls to try_cast will return None and calls to cast will panic (so these casts will likely not be used). And then if the implementation is changed then those calls will start succeeding.

@nicoburns
Copy link
Contributor Author

@emilio had some doubts about this in #46

I somewhat agree that this could be confusing. I think this is Euclid's fault for having "cast" as it's name/mechanism for doing this. We could potentially add a different method/mechanism. Taffy's "geom" containers have map methods which would allow us to do things like size.map(Au::from_f32_px). Which is not quite as short as casting, but it's more explicit.

Although IMO Au::new is worse in this regard. I would expect Au::new(120).as_f32_px() to be 120.0 not 2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants