godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add functions similar to `type_convert` for every individual type that a Variant can be converted to. #10320

Open pineapplemachine opened 1 month ago

pineapplemachine commented 1 month ago

Describe the project you are working on

A project where it is helpful to have GDScript type safety warnings/errors turned on in project settings.

Describe the problem or limitation you are having in your project

In 4.3, since the behavior of UNSAFE_CAST warning/error has been improved to recognize variant_value as [Type] casts as unsafe, there is now no way at all to unbox a Variant value that Godot can recognize as safe.

See also the discussion here: https://github.com/godotengine/godot/issues/94918

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A new type_convert function would be added for every type that a Variant can be converted to.

A new function would also be needed in the future for any additional types that can be boxed by a Variant.

type_convert_bool(value: Variant) -> bool
type_convert_int(value: Variant) -> int
type_convert_float(value: Variant) -> float
type_convert_string(value: Variant) -> String
type_convert_vector2(value: Variant) -> Vector2
type_convert_vector2i(value: Variant) -> Vector2i
type_convert_rect2(value: Variant) -> Rect2
type_convert_rect2i(value: Variant) -> Rect2i
type_convert_vector3(value: Variant) -> Vector3
type_convert_vector3i(value: Variant) -> Vector3i
type_convert_transform2d(value: Variant) -> Transform2D
type_convert_vector4(value: Variant) -> Vector4
type_convert_vector4i(value: Variant) -> Vector4i
type_convert_plane(value: Variant) -> Plane
type_convert_quaternion(value: Variant) -> Quaternion
type_convert_aabb(value: Variant) -> AABB
type_convert_basis(value: Variant) -> Basis
type_convert_transform3d(value: Variant) -> Transform3D
type_convert_projection(value: Variant) -> Projection
type_convert_color(value: Variant) -> Color
type_convert_string_name(value: Variant) -> StringName
type_convert_node_path(value: Variant) -> NodePath
type_convert_rid(value: Variant) -> RID
type_convert_object(value: Variant) -> Object
type_convert_callable(value: Variant) -> Callable
type_convert_signal(value: Variant) -> Signal
type_convert_dictionary(value: Variant) -> Dictionary
type_convert_array(value: Variant) -> Array
type_convert_packed_byte_array(value: Variant) -> PackedByteArray
type_convert_packed_int32_array(value: Variant) -> PackedInt32Array
type_convert_packed_int64_array(value: Variant) -> PackedInt64Array
type_convert_packed_float32_array(value: Variant) -> PackedFloat32Array
type_convert_packed_float64_array(value: Variant) -> PackedFloat64Array
type_convert_packed_string_array(value: Variant) -> PackedStringArray
type_convert_packed_vector2_array(value: Variant) -> PackedVector2Array
type_convert_packed_vector3_array(value: Variant) -> PackedVector3Array
type_convert_packed_vector4_array(value: Variant) -> PackedVector4Array
type_convert_packed_color_array(value: Variant) -> PackedColorArray

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

With the use of these functions, it becomes possible to convert Variant values to other typed values without the GDScript parser believing those conversions to be unsafe.

For example:

var variant_value: Variant = 123
var int_value: int = type_convert_int(variant_value)

This is in contrast to the following options, which may work at runtime if the programmer is certain of the type of the value boxed by the Variant, but which are considered unsafe by Godot's type system:

var variant_value: Variant = 123
var int_value: int = variant_value
var variant_value: Variant = 123
var int_value: int = variant_value as int

If this enhancement will not be used often, can it be worked around with a few lines of script?

There is currently no workaround except to ignore Godot's type safety warnings when working with Variants.

Is there a reason why this should be core and not an add-on in the asset library?

There should be a built-in way to safely unbox Variants, rather than requiring a GDExtension for this.

dalexeev commented 1 month ago

In 4.3, since the behavior of UNSAFE_CAST warning/error has been improved to recognize variant_value as [Type] casts as unsafe, there is now no way at all to unbox a Variant value that Godot can recognize as safe.

I think this is not a sufficient justification. Yes, we have things like maxi() and maxf(), but this unnecessarily clutters the global scope. I would prefer a generic function:

type_convert[T](from: Variant) -> T

Or we can solve this on the GDScript side by changing the as operator behavior or introducing a new cast operator.

If this enhancement will not be used often, can it be worked around with a few lines of script?

There is currently no workaround except to ignore Godot's type safety warnings when working with Variants.

You can make these functions in your Utils class:

static func type_convert_bool(value: Variant) -> bool:
    return type_convert(value, TYPE_BOOL)
pineapplemachine commented 1 month ago

I would prefer a generic function:

I agree that this would be better, once generic functions are supported.

Or we can solve this on the GDScript side by changing the as operator behavior

I do think it's useful to have as behave as a more conventional casting operator, producing an error for invalid conversions, and something else for converting with a fallback value. (In fact, I suppose it would be better if the suggested conversion functions also accepted an optional fallback: T = [default value of T] argument.)

You can make these functions in your Utils class:

Oh, I had overlooked that implicit conversion of a return value is not marked as unsafe as it is with assignments. Fair enough.