-
Notifications
You must be signed in to change notification settings - Fork 323
Add examples for type conversion #1123
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 examples for type conversion #1123
Conversation
examples/type_conversion.rs
Outdated
]; | ||
unsafe { | ||
let a_u8: Array<u8, _> = a_f32.mapv(|element| element.to_int_unchecked::<u8>()); | ||
assert_eq!(a_u8, array![1, 0, 254, 255, 254, 253, 0, 0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written below, I don't think these results can be relied on outside of std
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I'll remove it.
I am not sure if this is too much for this MR, but similar to how we avoided the unchecked conversion, maybe we should include the infallible conversions based on While |
Actually, I really like the idea, so thank you for pointing this out @adamreichold ! I completely refactored the examples so that Also, I was not able to figure out why the CI fails for my patch - how is the file that I added different from the other examples? If you can point me in the right direction, I'd be happy to fix it. |
254.1, // rounded down to 254 by cutting the decimal part | ||
-1.0, // saturated to 0 on the lower end | ||
f32::INFINITY, // saturated to 255 | ||
f32::NAN, // converted to zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be sure @adamreichold - this is not UB, right? Otherwise, I'd at least add a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is well-defined, c.f. https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions (Generally speaking, Rust code that does not use unsafe
will not have undefined behaviour. That casts of floating point numbers used to have was a bug which was resolved by fixing the saturating semantics.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A great, thank you for the link!
Your example has a #[cfg(not(feature = "approx"))]
fn main() {} |
examples/type_conversion.rs
Outdated
// | ||
// Below, we illustrate three different approaches for the actual conversion | ||
// in the closure. | ||
// - `From` ensures perfect conversions known at compile time and is the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the official document refers to this as "lossless" instead of "perfect".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to be missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, now I understood the comment - thanks! Should be updated now.
examples/type_conversion.rs
Outdated
// in the closure. | ||
// - `From` ensures perfect conversions known at compile time and is the | ||
// best default choice. | ||
// - `TryFrom` either converts data perfectly or panics, ensuring that the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TryFrom
performs a fallible conversion, whether the program panics or not is then up to the user of the trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
examples/type_conversion.rs
Outdated
assert_eq!(a_u8, array![120u8, 8u8, 0u8]); | ||
|
||
// Simple upcasting with `as` | ||
// Every `u8` fits perfectly into a `u32`, therefore we do not need to take |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In realistic programs, one often starts with this reasoning but especially with type interference the involved types can change and as
becomes lossy even if it started out lossless. Hence, I think From
should be recommended whenever possible. If the types changes and cannot be converted losslessly any more, compilation will fail and one can still decided to "downgrade" to a cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good to have examples with as, so we should have some
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do agree that there should be examples, but I would recommend against choosing one where From
could be used instead. The as
example should rather do a potentially lossy conversion that cannot be done using From
probably with a comment to use this if TryFrom
is a performance issue or does not apply.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm the prime example for me would be f32 as i32
- but already have an example from floats to integers to show the saturating cast. With the updated comments/documentation, do you think it is fine or which example would you like to see in particular @adamreichold ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think it is fine or which example would you like to see in particular @adamreichold ?
I think it is fine. The other one rather common example (for example when working with integer coordinates on a grid) is between signed and unsigned types, e.g. usize as isize
(which can fail as usize::MAX > isize::MAX as usize
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thank you for the example, I included it in type_conversion.rs
🙂
//! NumPy | `ndarray` | Notes | ||
//! ------|-----------|------ | ||
//! `a.astype(np.float32)` | `a.mapv(\|x\| f32::from(x))` | convert `u8` array infallibly safe to `f32` array with `std::convert::From`, generally recommended | ||
//! `a.astype(np.uint8)` | `a.mapv(\|x\| u8::try_from(x))` | try to convert `i8` array to `u8` array, panic if any value cannot be converted perfectly at runtime (e.g. negative value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would result in an array of Result
s as the intended .unwrap()
is missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes absolutely, thank you!
I don't want to pile on instructions here, but I think keep it simple. For my sake, we can write anything into the example file (but word more carefully in the documentation - carefully means with facts). Maybe this link is useful to link to (std's docs for the |
//! - The `as` keyword performs a *saturating* cast since Rust 1.45 and should | ||
//! only be used for safe, clear conversions such as upcasting. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The note about saturating casts is for floats only, not for all as
conversions. We need a doc link anyway to explain the full rules, I think.
//! - The `as` keyword performs a *saturating* cast since Rust 1.45 and should | |
//! only be used for safe, clear conversions such as upcasting. | |
//! - The `as` keyword performs a cast which can convert between primitive types. [Other documentation links here] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a great suggestion, thank you!
I think that it wasn't until Rust 1.45 did not help its reputation. Leaving this aside, I see two kinds of problems when using
|
Ah yes, that makes sense, thanks! |
Thank you both @adamreichold and @bluss for the thorough review! I tried to strike a balance between warning about potential issues when using |
It's good. I could send in my own edits later if I have some opinions about wording. |
Alright, sounds good! 🙂 And I hope I finally got the feature guards right for CI ^^ Sorry for the mess-up there, should have run the |
examples/type_conversion.rs
Outdated
// | ||
// Below, we illustrate three different approaches for the actual conversion | ||
// in the closure. | ||
// - `From` ensures perfect conversions known at compile time and is the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to be missing?
Thank you for proof-reading @bluss ! I included all your comments - what exactly do you mean seems to be missing? An example with Unfortunately, I cannot reproduce the error in the |
- Add `type_conversion.rs` to illustrate some common conversions. - Update the documentation for numpy users.
Thanks! |
type_conversion.rs
to illustrate some common conversions.