-
Notifications
You must be signed in to change notification settings - Fork 211
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
Re-organize Variant conversion methods #819
Conversation
Inherent conversion methods are now genericized into `new`, `to`, `try_to`, and `coerce_to`, to reduce the clutter and make other `Variant` methods more discoverable in docs. - `new` replaces the `from_*` methods. The original version that returns nil variants is renamed to `Variant::nil`, to better reflect its behavior. - `to` and `try_to` replaces the `try_to_*` methods, with naming more consistent with the rest of the Rust ecosystem. - `to_object` and `try_to_object` are kept for convenience around `Ref`. - `coerce_to` replaces the `to_*` methods. A new trait `CoerceFromVariant` is introduced and implemented for all GDScript built-in types. - Documentation is updated around convertion methods/traits to highlight what is used for exported methods. Close godot-rust#774
Most of this should be consistent with discussions in #774 and #dev. There might be an argument against the |
Great idea, I like the approach of separating pub fn new<T>(from: T) -> Variant;
pub fn nil() -> Variant;
pub fn to<T>(&self) -> Option<T>;
pub fn try_to<T>(&self) -> Result<T, FromVariantError>;
pub fn coerce_to<T>(&self) -> T;
pub fn to_object<T>(&self) -> Option<Ref<T, Shared>>;
pub fn try_to_object<T>(&self) -> Result<Ref<T, Shared>, FromVariantError>;
These ones are really satisfying 🙂 |
tryBuild succeeded: |
This looks good! Only point is as mentioned the name As discussed, I'll leave this for a few days for comments. If no more ideas or change requests come up, I'd merge it then. Worst case the constructor can still be renamed later (if necessary with deprecation). |
Something I noticed while using the new API: the generic functions use type inference due to their nature. This can be nice, but it also makes things less explicit. In combination with the "weak type safety" for numeric types, the new API adds potential for bugs that wasn't present before. To show the principle: let unsigned = u64::MAX; // value: 18446744073709551615
let variant = Variant::new(unsigned); // value: -1
let back: u8 = variant.to().unwrap(); // value: 255 The same in a more real-world use case: fn accept_int(int: Option<i16>) { ... }
let variant = Variant::new(-15);
accept_int(variant.to()); // all good
// Later: user refactors i16 -> u16
// Everything compiles, runtime bug
// Note: this would not have happened without type inference:
accept_int(variant.to::<i16>()); // compile error A completely unrelated scenario: let dict: Dictionary = ...;
let var = gd::Variant::new(dict); // looks like move
println!("Dict: {:?}", var); // works -- not moved! The examples are not a direct problem of this API, but rather of the underlying traits In rare cases, implicit narrowing/signed-change can be desired behavior (e.g. This is not per se an argument against the new API, the added genericity is nice. But we have to be aware that it encourages patterns which are not very typical for Rust. In the future (not necessarily this PR) we could a) document the behavior and hint to the |
This is how Between
I'm not sure if we shouldn't accept some runtime errors as par of the course when working with dynamic typing through
I'm not sure I understand what you mean by this?
I agree that it can be helpful to have checks for overflow/underflow when converting to smaller integers. More documentation is also always good. |
Yes, that's exactly what I meant with "The examples are not a direct problem of this API, but rather of the underlying traits To/FromVariant [...]". 🙂 The
The problem is, there is no runtime error. The conversion silently narrows integers, i.e. produces bugs that can propagate further. In case you meant bugs with errors, maybe you're right and some should be expected. On the other hand, the presence of I understand it's not that easy, because the
Sorry, my fault. Apart from the typo, I had an (this code, to be clear) let dict: Dictionary = unimplemented!();
let var = Variant::new(dict);
println!("Dict: {:?}", dict); |
Going to merge this; the bors r+ |
Build succeeded: |
Inherent conversion methods are now genericized into
new
,to
,try_to
, andcoerce_to
, to reduce the clutter and make otherVariant
methods more discoverable in docs.new
replaces thefrom_*
methods. The original version that returns nil variants is renamed toVariant::nil
, to better reflect its behavior.to
andtry_to
replaces thetry_to_*
methods, with naming more consistent with the rest of the Rust ecosystem.to_object
andtry_to_object
are kept for convenience aroundRef
.coerce_to
replaces theto_*
methods. A new traitCoerceFromVariant
is introduced and implemented for all GDScript built-in types.Close #774
Rare PR with negative LoC