Implement AnyArray, a covariant typed/untyped array#1422
Conversation
|
API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1422 |
|
Reading the description, this seems like a great solution to me. I'll be trying it out as soon as I find myself using Godot arrays in my current project :P
If there's not one already, a simple method that casts to a typed array infallibly, creating a new one if needed and simply returning the current one if possible would be nice to have. It'd be simple enough with the |
|
Thanks for the feedback!
"Infallible" isn't really possible, |
for clarification, does this add both
i can imagine this might still break for something like |
Typo.
Good point. There aren't many situations in which |
Yeah that's more what I meant, sorry. 'Downcast' (copying if needed) to a typed-untyped Array :P |
When passing &varray[...] to a method accepting &OutArray, rustc will fail compilation, as the involved types don't seem to be eligible for auto-deref coercion. The explicit `as` cast gives the compiler the necessary type hints.
9dfe7a5 to
2be0c28
Compare
This is a second attempt at addressing the "
OutArrayproblem" from #806.Closes #727.
The problem
In GDScript, the subtyping relationship is modeled incorrectly for arrays:
The solution:
AnyArrayThis PR introduces a new type
AnyArray, which can store any array, typed or untyped.AnyArraythus provides operations that are valid regardless of the type, e.g.len(),clear()orshuffle().Methods, which can be more concrete on
Array<T>by usingT(e.g.pick_random() -> Option<T>), exist on both types.AnyArraydoes not provide any operations where data flows in to the array, such aspush()orinsert().Conversions
Deref coercion allows every
&Array<T>to be treated as&AnyArray. This is true even forT=Variant.To upcast
Array<T>values toAnyArray, useArray::into_any(). You can explicitly downcast anAnyArrayusingtry_into_typed()andtry_into_untyped().Compatibility
I've put a lot of emphasis on keeping existing code working as much as possible:
engine_method(&variant_array)keeps working due toDerefcoercion.let a: VariantArray = engine_method()also keeps working. Methods return untyped arrays.virtual_method(a: VariantArray)signature stays as well.virtual_method() -> VariantArraynow returnsAnyArray. This is breaking but now allows returning both typed and untyped arrays, usinginto_any()conversion.array!,varray!expressions keep working as-is and as expected.New features:
Array<T>forT != Variant) for engine parameters that previously only acceptedVariantArray.InnerArraymethods that return a possibly-typed array, and currently need a combination of generics +unsafeto be used correctly.Open points
Naming
AnyArraymeans "it can represent any array, either typed or untyped". The type erasure is somewhat expressed through "any". Not 100% happy with it though...OutArraydue to data only being allowed to flow out of the array. Kotlin usesoutvariance. Important, this is not read-only.clear(),shuffle()etc. are perfectly fine, as they don't need to insert an unknown element type.CovArrayfor "covariant array", but this gets a bit more towards type theory and may not be obvious at all.Complexity
It does introduce a new concept that doesn't exist in GDScript, but allows for more robust code when dealing with untyped or maybe-typed arrays. There are no more no-ops or panics when accidentally inserting into arrays.
An alternative would be to just allow
Array<T>to deref toVariantArray(maybe needs separate type?) and thus model GDScript's (broken) relations. It would mean we have to introduce panics or no-ops when theVariantArrayis in reality pointing to a typed array.