Recently I had this conversation on bugreport.apple.com with Apple engineer. I filled report that application will crash when initialized with bigger value than can be stored in initialized object, as a respond
“determined that this issue behaves as intended. UInt8(u) is a checked conversion. 773162 is not representable as an 8-bit unsigned integer, so it traps.”.
Well.. I disagree. I think that code should not crash in runtime if there are situations where size of type overflow (in bits right?) is obvious. It should not compile, or this should be not possible at all.
Situation looks like this:
let big:UInt64 = 576460752303423504
let small:UInt8 = UInt8(big) // crash
because integer value 576460752303423504 need more than 8 bits to represent value and Swift (by design) do not truncate bits (that’s fine). The cause is:
init(_ v: UInt64)
for a sake, why UInt8 have this initializer at all? it potentially crash app. Situation is grotesque when I think about Int type. I know that Int is 32 or 64 bit wide. So this example
let a:Int = 576460752303423504
will or will not crash depends on device it’s run on (iPhone5 is 32-bit, iPhone 5s is 64-bit). Why the hell I would allow this? I don’t want actually… but I can.
I stand on a position that if programmer need to cast to smaller type it should be done by programmer like this:
let truncated = UInt8(576460752303423504 & 0xFF) // truncate bits intentionally. Size of AND operation is clear (1 byte) so no crash. No crash, no problem.
IntMax to the rescue
Generally it is good idea always operate with values as wide as possible (UIntMax, IntMax). IntMax is currently defined as Int64 so it’s wide enough. Of course. if I’m on 32-bit device is not so good idea because my Int is 32-bit, my IntMax is 64-bit so there may be case where I initialize my Int value and that will be overflow (ouch).
Working with generic values is possible thanks to protocol SignedIntegerType/UnsignedIntegerType where I found handy function “from”
class func from(_: IntMax) -> Self
very usable. It is implemented by all Int based structs so after value is cast to IntMax, result can be used to initialize Generic type in safe manner
let result = T.from(IntMax(1)) // T: SignedIntegerType
IntegerArithmeticType
Just to mention about this protocol. Overflow safe functions that language designers gave us. Very handy. Crash sometimes. Allows do basic math with overflow in scope.
let result = UInt8.addWithOverflow(255, 2) // = 1, true
is equivalent of this
let result = (255 as UInt8) &+ 2 // 1
but with information about overflow.
Update for Xcode 6.1 beta
Issue has been somehow addressed in Xcode 6.1 beta with truncatingBitPattern initializers, like this one for UInt8:
init(truncatingBitPattern: UInt16)
Construct a Int8 having the same bitwise representation as the least significant bits of the provided bit pattern.