I have the struct Vector:
struct Vector{
var X : Measurement<Dimension>
var Y : Measurement<Dimension>
var Z : Measurement<Dimension>
...
And I create new object, for example:
let test = Measurement(value: 1.5, unit: UnitLength.inches) as Measurement<Dimension>
var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters))
Everything works fine. But if try to use variable from other class, I got the error: "Cannot convert value of type ‘Measurement’ to type ‘Measurement’ in coercion"
final class SettingsManager{
...
var test = Measurement(value: 1.5, unit: UnitLength.inches)
... }
class Calculator {
...
let test = SettingsManager.shared.test as Measurement<Dimension>
var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters))
I tried "as!" and got
Cast from 'Measurement<UnitLength>'
to unrelated type 'Measurement<Dimension>'
always fails
The same variable declared in this class works fine as I showed above. What I did wrong?
3
Answers
When you are declaring the
test
variable inside theSettingsManager
you are not explicitly mentioning a type. So the swift’s type inference algorithm decides the type of the variable depend on the second parameter inMeasurement(...)
.In this case
test
has the type ofMeasurement<UnitLength>
. If you used passedUnitArea.acres
as the second parameter your variable will have the typeMeasurement<UnitArea>
. NotMeasurement<Dimension>
.If you mention the type explicitly as
Measurement<Dimension>
in the first place it will solve the issue.Now lets check why your casting fails. You can cast a type to its sub type or super type.
UnitLength
is a subtype ofDimension
. So you can cast between them.But
Measurement<UnitLength>
is NOT a subtype ofMeasurement<Dimension>
. Why ?The reason is simple. Generics are invariant. This means that even if a generic type wraps a subtype, it doesn’t make it a subtype of a generic wrapping its superclass.
For more clarification about the above paragraph read this.
A
Measurement<UnitLength>
is not a kind ofMeasurement<Dimension>
. These are unrelated types. It only works here:because the
Measurement(...)
calls here are actually creatingMeasurement<Dimension>
s, since that is what is being expected at the callsite.as Measurement<Dimension>
tells it that you want aMeasurement<Dimension>
, and the parameter type ofx
andz
are alsoMeasurement<Dimension>
. The type inference algorithm is smart enough to see that you must also meanMeasurement<Dimension>(...)
.Measurement<Dimension>.init
takes aDimension
as its second parameter, andUnitLength
is a subtype of that, so no problems there.On the other hand, in
SettingsManager
, you declaredtest
like this:Nowhere here did you mention
Measurement<Dimension>
, so the type inference algorithm just uses the second parameter to infer that you must meanMeasurement<UnitLength>
, and so the type oftest
isMeasurement<UnitLength>
.If you just add
as Measurement<Dimension>
it should work.
However, wouldn’t it make more sense to for all three components of the vector to have the same type of unit?
This is just to lay to rest the question of why this is legal:
This shouldn’t even be a question; it’s just the normal substitution principle. Let’s take a simpler way of expressing it. What if you don’t cast?
Then
test
is inferred asMeasurement<UnitLength>
. Now let’s cast:That’s legal. Why? It’s not because
Measurement<UnitLength>
is a subtype ofMeasurement<Dimension>
. It isn’t! It’s because UnitLength is a subtype of Dimension. It is as if you had saidDo you see? You didn’t actually cast
Measurement<UnitLength>
toMeasurement<Dimension>
; that is impossible. You cast UnitLength up to Dimension, which is perfectly possible. It’s no different than if you had saidYou’re always allowed to substitute a subtype where a supertype is expected. The difficulty in the original question is that
Measurement<UnitLength>
is not a subtype ofMeasurement<Dimension>
(because Swift generics are not automatically covariant over their parameterized type).