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

Add better numerical type conversion functions #52

Closed
LeshaInc opened this issue Apr 29, 2020 · 5 comments
Closed

Add better numerical type conversion functions #52

LeshaInc opened this issue Apr 29, 2020 · 5 comments

Comments

@LeshaInc
Copy link

Writing vec.numcast().unwrap() is really noisy, especially where I don't expect any conversion errors and don't want to handle them. To avoid unnecessary checks and panics I keep writing vec.map(|v| v as f32) which is still not ideal...

So I suggest adding a new function for conversion between different numerical types, using num_traits::cast::AsPrimitive. In most cases, precision loss and overflowing isn't something you should worry about, especially in graphics programming (e.g. converting u32 framebuffer extent to f32).

Perhaps, the old numcast function should be renamed to try_cast and a new panicking cast be introduced to match euclid behaviour. But that's another question...

@yoanlcq
Copy link
Owner

yoanlcq commented Apr 30, 2020

Yes I agree. Back then, num_traits::cast::AsPrimitive wasn't a thing, and as you mention, it's possible to get away with e.g vec.map(|v| v as f32) (which I think is quite good actually, from personal experience it's hard to get more concise than that with other languages... I'm thinking about Clang's __builtin_convertvector for instance).

But it's true that we now have num_traits::cast::AsPrimitive and should make good use of it. The possibility of reducing vec.map(|v| v as f32) to e.g vec.as_() is attractive and I'll look into it.

I think numcast should keep its name, since it matches the associated trait name, and even if I wanted to rename it, it would be a breaking change.

and a new panicking cast be introduced to match euclid behaviour.

I must confess I did not understand what you mean in this part; A bit more detail would help (thanks in advance and sorry!)

@yoanlcq
Copy link
Owner

yoanlcq commented Apr 30, 2020

This should be fixed; Although I close the issue with my commit, feel free to add to the discussion regardless.

I'll push a new version with this change very soon.

@LeshaInc
Copy link
Author

I must confess I did not understand what you mean in this part; A bit more detail would help (thanks in advance and sorry!)

I mean, euclid has try_cast and cast, which is basically try_cast().unwrap(). Not sure if it's necessary, but it saves a few keystrokes if you don't want to handle the error, but an overflow would break the subsequent code, so you want to keep the check.

Also, how do you think about adding as_iXX/as_uXX in addition to as_()? Looks way better than as_::<i32>(), but that's quite a lot of new functions.

@yoanlcq
Copy link
Owner

yoanlcq commented Apr 30, 2020

Oh ! Right, I did not think of euclid as in "the euclid crate", so I wondered what "euclid behaviour" could mean...

I mean, euclid has try_cast and cast, which is basically try_cast().unwrap()

Okay, I see. I don't think I would like an equivalent to euclid's cast in vek, because it appears to me that having to unwrap() explicitly is somewhat more "idiomatic" Rust and therefore normally preferred. The standard library, for instance, does not have APIs which unwrap() options or results implicitly (AFAIK).

An advantage of explicit unwrapping is that you can quickly pinpoint the parts of your codebase that could panic because of this, which is somewhat harder when it's hidden in a library function.
I mean, I do also understand the appeal of lower verbosity, at the end it's a matter of personal preference, but when writing a library I like to make sure it's a close to the language's philosophy as possible.
(I'm not saying that euclid's cast is blasphemy either, it's just not a thing I would do. I'd rather users have to type out unwrap() or ? as they need).

Also, how do you think about adding as_iXX/as_uXX in addition to as_()? Looks way better than as_::(), but that's quite a lot of new functions.

From personal experience, this isn't ideal; bloating the library with as_foo for every primitive type isn't worth the benefit of avoiding typing as_::<foo>, which is only 4 characters longer, assuming you have to specify the turbofish at all (in some cases the type is inferred, for instance).

To me, it's a balance between "there should be only one way to do this kind of task" (e.g keep as_()) and "this task is performed so often that it deserves special shortcuts" (e.g add as_iXX() etc).

I'm imagining adding as_xxx() for all (something like 12?) primitive types; do it for vectors, then matrices, then geometric shapes... After all, they now do implement as_(), so we might as well be consistent.
That would quickly add up, again just to save 4 characters which can even be completely omitted in some contexts... The "balance" isn't right, if that makes sense.

@LeshaInc
Copy link
Author

LeshaInc commented May 1, 2020

I understand. For me, vec.as_() is more than enough, thanks for adding.

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

No branches or pull requests

2 participants