-
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
Make Variant conversions hard to misuse #774
Comments
At the moment I'm thinking users should just convert types in Rust if that's what they want, as opposed to letting Godot return its so-called "best effort" conversion... Gives them more control, better documentation, possibly better performance, and access to more idiomatic API's. However, I also feel like there is value in exposing all of the functions that Godot gives us, in case some users want to use them... Converting from GDScript to Rust is easier if the API and behavior matches, but learning idiomatic Rust might be easier if the API is adapted... |
As I mostly program in rust and attempt to use the One note is that I always took the semantics of I see two specific use-cases
At the moment I essentially always convert in the following manner. fn set_value(&mut self, _: &Node, variant: Variant) {
if let Some(value) = variant.try_to_int64() {
self.value. = value;
} else {
godot_error!("Value is not int64");
}
} |
For the past couple days I have been learning to use these bindings and my experience is that there are a couple pitfalls which affected me and without being informed about them I would not have been able to know what was wrong. One major variant conversion issue was being unable to take an array within an array of arrays containing strings and convert it to a |
@super-continent Is it about conversion from If |
Edited for more clarification, it does seem mainly related to variant conversions being a bit confusing |
Their original message in Discord mentioned the |
Would this be a good place to discuss the idea of replacing |
@Waridley I would open a separate issue for that. |
Some prior art for explicit coercion: |
819: Re-organize Variant conversion methods r=Bromeon a=chitoyuu 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 #774 Rare PR with negative LoC Co-authored-by: Chitose Yuuzaki <[email protected]>
There are multiple ways to convert
Variant
into certain types:and back:
Implementation (after cargo expand)
Current behavior
Mostly the conversion to types is interesting.
There are two behaviors:
to_i64()
-- use GDNative APIgodot_variant_as_int
, emulating GDScript implicit conversionstry_to_i64()
,i64::from_variant()
-- only convert if the variant indeed has exactly this typeIn Rust being a strongly typed language with explicit conversions, case 1. can sometimes be surprising, especially if a conversion silently fails and an arbitrary default value is chosen. A while ago I tested how values constructed in GDScript are converted using Rust
Variant
's conversion methods:Conversion tests
These are the good cases and more or less expected, but it gets problematic when a type is not representable in the target type, and a default like
0
,false
,""
etc. is chosen (TODO: include those tests).Possible changes
My suggestion is to rename
try_to_*()
methods toto_*()
, so the most straightforward way to convert returns anOption
, making the caller aware of potential errors and handling them explicitly.For the existing
to_*()
methods, we have multiple options:float
->int
conversion)coerce_to_*()
orto_*_or_default()
convert_to_*()
, change return type toOption<T>
and only returnSome(value)
on meaningful conversions. That would meanString("3.2")
->f32(3.2)
would be OK, butString("3.2k")
->f32(0.0)
would not happen. This will likely need a lot of effort and checking cases manually.VariantDispatch
enumIndependently of what we end up with, we should improve documentation for
Variant
:#[export]
method parameters and return types usesVariantDispatch
API and recommend its usage withmatch
andif let
The text was updated successfully, but these errors were encountered: