Reputation: 28516
What is proper syntax for using captured value in nested closure?
I have following working code for calculating CRC32 from integer value using zlib
library.
func testCrc()
{
var x: UInt32 = 0xffffffff
let result = withUnsafePointer(to: &x, {
$0.withMemoryRebound(to: Bytef.self, capacity: 4) {
crc32(0, $0, 4)
}
})
XCTAssertEqual(0xffffffff, result)
}
I want to create standalone generic function that can calculate CRC32 from any value. In order to do that, besides the value itself I have to also calculate and pass it's size, so I cannot use explicit size - 4 - like I used in above code.
But I am having trouble finding correct syntax to pass calculated size to inner closure.
func calculateCrc32<T>(_ crc: UInt, value: inout T) -> UInt
{
let size = MemoryLayout.size(ofValue: value)
let result = withUnsafePointer(to: &value, {
$0.withMemoryRebound(to: Bytef.self, capacity: size) {
crc32(crc, $0, size) // error
}
})
return result
}
Above code shows rather confusing compiler error for parameter $0
:
Cannot convert value of type 'UnsafeMutablePointer<_>' to expected argument type 'UnsafePointer!'
Confusing, because if I replace crc32(crc, $0, size)
with crc32(crc, $0, 4)
compiler is not complaining and function works properly for values with size of 4 bytes.
How to resolve above issue?
Upvotes: 4
Views: 190
Reputation: 539745
The error message is misleading. Your code is almost correct, the
"only" problem is the last argument of crc32()
which needs to
be an uInt
:
func calculateCrc32<T>(_ crc: UInt, value: inout T) -> UInt
{
let size = MemoryLayout.size(ofValue: value)
let result = withUnsafePointer(to: &value, {
$0.withMemoryRebound(to: Bytef.self, capacity: size) {
crc32(crc, $0, uInt(size))
}
})
return result
}
If you call crc32(crc, $0, 4)
then the "integer literal" 4
is passed as the last argument, and the compiler infers its type
as uInt
to match the function definition.
It does not compile with crc32(crc, $0, size)
because Swift does
not implicitly convert between types.
Alternatively, use numericCast()
, which is a generic function which converts between different signed and unsigned integer types (and traps on overflow).
I would also suggest to make a local variable copy of the value instead
of using an inout
parameter, that makes it easier to call the
function:
func calculateCrc32<T>(_ crc: UInt, value: T) -> UInt {
var value = value
let size = MemoryLayout.size(ofValue: value)
let result = withUnsafePointer(to: &value, {
$0.withMemoryRebound(to: Bytef.self, capacity: size) {
crc32(crc, $0, numericCast(size))
}
})
return result
}
Upvotes: 4