I have an app which uses Metal and I find myself having to work with pointers frequently.
The user experience of pointers in Swift is utterly dreadful.
UnsafeMutablePointer
, UnsafePointer
, UnsafeMutableBufferPointer
, UnsafeBufferPointer
, UnsafeMutableRawPointer
, UnsafeRawPointer
, UnsafeMutableRawBufferPointer
, UnsafeRawBufferPointer
.
Did I get them all? This is just comically bad.
When I work with pointers, I know it's unsafe, I don't need or want the language constantly holding my hand and slowing me down while I have to keep track of these types.
I often find myself dropping to ObjC or C++ just so I don't have to deal with this garbage. Or I find myself just auto-completing my way to getting something working.
At least it's gotten better after working with swift for years now. I can read the tea leaves of compiler errors.
Why was the wheel of pointers reinvented? C++ could have had a mess of high-level pointer types of no use just like Swift. Instead they have super useful ones like unique_ptr
and shared_ptr
.
Rust, AFAICT, does not have a gaggle of pointer types.
Most swift code doesn't have to deal with pointers, so why try to make them as safe as possible?
Why can't we have, perhaps Pointer<T>
and MutablePointer<T>
and call it a day? A pointer to some bytes would be Pointer<Int8>
. And just have good old pointer arithmetic and subscripting. C pointers baby!
Can I build those pointers on top of swift's mess of pointer-wank?
pointer.advanced(by: 1)
instead of pointer+1
. Are you kidding me? Ok, maybe it does have the operator, I didn't actually check.
rawPointer.bindMemory(to: Int.self, capacity: count)
instead of (int*) rawPointer
. Is Swift just trying to make pointers so unpleasant to work with that I will stick to safe APIs? Which I can't of course.
assumingMemoryBound
(the verbosity!) vs bindMemory
? It's all just the same pointer under the hood, right?
This rather awful verbose MemoryLayout<T>.size
instead of sizeof(T)
.
And then of course there's withUnsafeBytes
. I'm sure eventually I'll have to nest that a few times to get multiple pointers. Why can't Swift guarantee a pointer is valid within the scope of what it's pointing to? If I do:
var array = [1,2,3]
var ptr = UnsafePointer<Int>(&array)
I get a warning about a dangling pointer. Is it so hard for Swift to ensure that ptr
is valid within the scope (like C/C++/ObjC others) or is this more hand-holding?
Let's look at some code which is importing a ModelIO triangle mesh into my app:
// Transform data.
vertexData.withUnsafeMutableBytes { buf in
let ptr = buf.baseAddress!.assumingMemoryBound(to: MCVertex.self)
for i in 0..<mesh.vertexCount {
var v = float3.zero
withUnsafeMutableBytes(of: &v) { ptr in
let start = i*stride+attr.offset
_ = vdata.copyBytes(to: ptr, from: start..<start+12)
}
let p = v * scale + translation
ptr[i].position.0 = p.x
ptr[i].position.1 = p.y
ptr[i].position.2 = p.z
}
}
In ObjC this would look something like:
MCVertex* ptr = (MCVertex*) vertexData.mutableBytes;
for(int i=0; i<mesh.vertexCount;++i) {
float3 v;
memcpy(&v, (char*)vdata.bytes + i*stride+attr.offset, 3*sizeof(float));
p = v * scale + translation;
ptr[i].position = p;
}
Now, maybe I could improve the Swift. But still, it's too complex!
Pointers are just not safe. Accept it and be at peace with it. Why try to fight it?
I know this reads like a rant. Please just tell me why I'm wrong.