Created
July 4, 2022 23:33
-
-
Save DylanSp/89d1cf1eb7b58c17626db55e9756c1a2 to your computer and use it in GitHub Desktop.
Phantom types in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"github.com/google/uuid" | |
) | |
// with this, the "value" field can't be accessed outside the package with PhantomID, | |
// which prevents PhantomID[T] from being compared to PhantomID[U] | |
// makes working with the ID any other way difficult, however | |
type PhantomID[T any] struct { | |
value uuid.UUID | |
} | |
type Dog1 struct { | |
id PhantomID[Dog1] | |
breed string | |
} | |
type Cat1 struct { | |
id PhantomID[Cat1] | |
numToes int | |
} | |
// embedding allows any caller to access the underlying UUID with id.UUID; | |
// which might allow incorrect ID comparisons, but allows the UUID to be read for other uses | |
type EmbeddedPhantomID[T any] struct { | |
uuid.UUID | |
} | |
type Dog2 struct { | |
id EmbeddedPhantomID[Dog2] | |
breed string | |
} | |
type Cat2 struct { | |
id EmbeddedPhantomID[Cat2] | |
numToes int | |
} | |
func main() { | |
dog1 := Dog1{ | |
// have to specify type parameter when instantiating PhantomID | |
id: PhantomID[Dog1]{ | |
value: uuid.New(), | |
}, | |
breed: "Beagle", | |
} | |
cat1 := Cat1{ | |
// have to specify type parameter when instantiating PhantomID | |
id: PhantomID[Cat1]{ | |
value: uuid.New(), | |
}, | |
numToes: 6, | |
} | |
// compiles; isn't rejected by typechecker | |
fmt.Println(dog1.id.value == cat1.id.value) | |
// is correctly rejected by typechecker; does not compile | |
// fmt.Println(dog1.id == cat1.id) | |
// compiles (correctly), returns true | |
fmt.Println(dog1.id == dog1.id) | |
dog2 := Dog2{ | |
// have to specify type parameter when instantiating EmbeddedPhantomID | |
id: EmbeddedPhantomID[Dog2]{uuid.New()}, | |
breed: "Golden Shepherd", | |
} | |
cat2 := Cat2{ | |
// have to specify type parameter when instantiating EmbeddedPhantomID | |
id: EmbeddedPhantomID[Cat2]{uuid.New()}, | |
numToes: 5, | |
} | |
// compiles; isn't rejected by typechecker | |
fmt.Println(dog2.id.UUID == cat2.id.UUID) | |
// is correctly rejected by typechecker; does not compile | |
// fmt.Println(dog2.id == cat2.id) | |
// compiles (correctly) | |
fmt.Println(dog2.id == dog2.id) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Future work: