Skip to content

Instantly share code, notes, and snippets.

@Catfish-Man
Last active April 12, 2022 00:26
Show Gist options
  • Save Catfish-Man/f79b35d3578a6d00ecccde36d0abe09e to your computer and use it in GitHub Desktop.
Save Catfish-Man/f79b35d3578a6d00ecccde36d0abe09e to your computer and use it in GitHub Desktop.
/*
This code is completely untested but should be close enough to correct to demonstrate the concepts
*/
struct RegularMonkey {
var bananaCount: Int
}
struct KlingonMonkey {
var bananaCount: Int
var opacity: Float //it has a cloaking device
}
var transparentMonkey = KlingonMonkey(bananaCount: 50, opacity: 0.5)
withUnsafePointer(to: transparentMonkey) { (basePointer:UnsafePointer<KlingonMonkey>) in
//Goal: treat `basePointer` as a pointer to a `RegularMonkey` instance, since their layout in memory is compatible, but also still be able to access the "subclass" pointer
// ❌ Failed attempt 1: just cast the pointer
/* Compiler is nice and warns us:
"warning build: 'unsafeBitCast' from 'UnsafePointer<KlingonMonkey>' to 'UnsafePointer<RegularMonkey>' changes pointee type and may lead to undefined behavior; use the 'withMemoryRebound' method on 'UnsafePointer<KlingonMonkey>' to rebind the type of memory"
*/
let monkey = unsafeBitCast(basePointer, to: UnsafePointer<RegularMonkey>.self).pointee
// ❌ Failed attempt 2: rebind the pointer
basePointer.withMemoryRebound(to: RegularMonkey.self, capacity: 1) { monkeyPtr in
let monkey: RegularMonkey = monkeyPtr.pointee //This is ok! We rebound the memory
let cloakedMonkey = basePointer.pointee //NOPE, we rebound that, can't use it safely anymore
}
// ✅🤮 Successful but awful attempt 3: repeatedly rebind the pointer
basePointer.withMemoryRebound(to: RegularMonkey.self, capacity: 1) { monkeyPtr in
let monkey: RegularMonkey = monkeyPtr.pointee //This is ok! We rebound the memory
monkeyPtr.withMemoryRebound(to: KlingonMonkey.self, capacity: 1) { cloakedMonkeyPtr in
let cloakedMonkey: KlingonMonkey = cloakedMonkeyPtr.pointee //This is ok! But, ew
}
}
// ✅ Successful attempt 4: use a raw pointer
let rawMonkeyPtr = UnsafeRawPointer(basePointer)
let monkey2: RegularMonkey = rawMonkeyPtr.load(as: RegularMonkey.self)
let cloakedMonkey: KlingonMonkey = rawMonkeyPtr.load(as: KlingonMonkey.self)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment