Reputation: 2516
I've isolated the following code in a playground. In the playground, I get the noted compile time errors:
class MyClass {
var weight: Double!
func toOunces() {
weight *= 0.035274 // 'Double!' is not identical to 'UInt8'
weight = weight * 0.035274 // works
}
func toGrams() {
weight /= 0.035274 // 'Double!' is not identical to 'Float'
weight = weight / 0.035274 // works
}
}
I was following an example online using NSCoder, where decodeDoubleForKey() as Double?
was being used, hence the implicitly unwrapped optional var weight: Double!
.
I'm debating how correct this is to even do, and I fixed it in my code.
My question is, why these compile time errors? Why does multiplication compare to UInt8 and division compares to Float? Is there something I've been missing for years regarding *= and /= ?
I'm still learning this, so I'm probably missing some basic property.
Upvotes: 2
Views: 379
Reputation: 42598
Well I think I have the answer, but you're not going to like it.
*=
is defined as such
func *=(inout lhs: UInt8, rhs: UInt8)
func *=(inout lhs: Int8, rhs: Int8)
func *=(inout lhs: UInt16, rhs: UInt16)
func *=(inout lhs: Int16, rhs: Int16)
func *=(inout lhs: UInt32, rhs: UInt32)
func *=(inout lhs: Int32, rhs: Int32)
func *=(inout lhs: UInt64, rhs: UInt64)
func *=(inout lhs: Int64, rhs: Int64)
func *=(inout lhs: UInt, rhs: UInt)
func *=(inout lhs: Int, rhs: Int)
func *=(inout lhs: Float, rhs: Float)
func *=(inout lhs: Double, rhs: Double)
/// multiply `lhs` and `rhs` and store the result in `lhs`, trapping in
/// case of arithmetic overflow (except in -Ounchecked builds).
func *=<T : _IntegerArithmeticType>(inout lhs: T, rhs: T)
func *=(inout lhs: Float80, rhs: Float80)
/=
is defined as
func /=(inout lhs: Float, rhs: Float)
func /=(inout lhs: Double, rhs: Double)
func /=(inout lhs: Float80, rhs: Float80)
/// divide `lhs` and `rhs` and store the result in `lhs`, trapping in
/// case of arithmetic overflow (except in -Ounchecked builds).
func /=<T : _IntegerArithmeticType>(inout lhs: T, rhs: T)
The /
and *
operators don't have an inout
specifier on their first parameter, were as /=
and *=
do. The other thing I notice is UInt8
is the first *=
operator and Float
is the first /=
operator.
Putting this all together, Double!
can be coerced into Double
, but cannot be coerced into inout Double
. In both cases the error message is picking the first version of the operator method to report in the error.
Upvotes: 1
Reputation: 64674
You can make the *= operator work by explicitly unwrapping the optional first like so:
func toOunces() {
weight! *= 0.035274
}
You can see why this is looking at how *= is defined.
func *=(inout lhs: Double, rhs: Double)
An implicitly unwrapped optional can't be passed as an inout parameter because it is a wrapper around a Double (or nil.)
Non inout arguments can be unwrapped automatically because the function just needs the value which can be extracted from the optional automatically rather than a reference to the actual value. That's why the * operator works for Double!
which is defined as such.
func *(lhs: Double, rhs: Double) -> Double
Adding the ! after weight in your code changes it from passing a reference to the optional to passing a reference to the Double encased in the Optional. If *= had a normal function syntax and we remove some sugar it might look like this:
func multiplyAssign (inout lhs: Double, rhs: Double){
lhs = rhs * lhs
}
var weight: ImplicitlyUnwrappedOptional<Double> = 0.0
multiplyAssign(&weight, 10.0) //trying to pass reference to an ImplicitlyUnwrappedOptional<Double> (error)
multiplyAssign(&(weight!), 10.0) //passing a reference to a Double
Upvotes: 4