Created
January 11, 2024 21:53
-
-
Save iliakonnov/728e108a94d011329f2550d94ae5dd76 to your computer and use it in GitHub Desktop.
This file contains 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 ifaces | |
import ( | |
"unsafe" | |
) | |
// Preparations | |
// --------------------- | |
// Basic interface `Iface` and a basic struct `Impl` implementing it | |
type Iface interface { | |
Foo(x int) string | |
} | |
type Impl struct{} | |
func (self *Impl) Foo(x int) string { | |
return "" | |
} | |
// Introducing vtables | |
// --------------------- | |
// IfaceVtable is a vtable containing pointers to all `Iface` methods. | |
// Single unique instance of this struct is generated for each type `TSelf` implementing `Iface`. | |
type IfaceVtable struct { | |
// For any given method `TSelf.Foo(...args) ...` vtable contains field `Foo` containing `func(*TSelf, ...args) ...` | |
Foo func(self unsafe.Pointer, x int) string | |
} | |
// IfaceWrapper allows to erase any concrete type `TSelf` wrapping it. | |
type IfaceWrapper struct { | |
vtable *IfaceVtable // Stores methods, e.g. { Foo(*TSelf, ...), ... } | |
self unsafe.Pointer // Stores *TSelf so we can call these methods | |
} | |
// Foo | |
// Now interface methods can be called like that. Note that we don't need to have concrete TSelf now. | |
func (val *IfaceWrapper) Foo(x int) string { | |
return val.vtable.Foo(val.self, x) | |
} | |
// Unsugaring down to plain vtables | |
// --------------------------------- | |
// Foo is just a simple function returning an interface: | |
func Foo() Iface { | |
impl := Impl{} | |
return &impl | |
} | |
// IfaceVtableForImpl contains a vtable for type `Impl` implementing interface `Iface`. | |
// It's basically a constant. | |
var IfaceVtableForImpl = IfaceVtable{ | |
Foo: func(self unsafe.Pointer, x int) string { | |
// Here we are guaranteed that `IfaceVtableForImpl` will be used only with `Impl` types. | |
// So we can safely convert untyped `unsafe.Pointer` to typed `*Impl`. | |
return ((*Impl)(self)).Foo(x) | |
}, | |
} | |
// FooUnsugared is a simple function returning an interface, but without any of Go's magic. | |
func FooUnsugared() IfaceWrapper { | |
impl := Impl{} | |
return IfaceWrapper{ | |
vtable: &IfaceVtableForImpl, | |
self: unsafe.Pointer(&impl), | |
} | |
} | |
// Showing why `Iface((*Impl) nil)` not equal to `nil` | |
// --------------------------------------------------- | |
// ReturningNil returns nil value wrapped into Iface | |
func ReturningNil() Iface { | |
var impl *Impl = nil | |
return impl | |
} | |
// ReturningNilUnsugared is the same, but unsugared. | |
// Note that vtable is not nil, because we do have vtable for type Impl implementing Iface. | |
// But the `self` pointer in wrapper is nil though. | |
func ReturningNilUnsugared() IfaceWrapper { | |
var impl *Impl = nil | |
return IfaceWrapper{ | |
vtable: &IfaceVtableForImpl, // <- not nil !!! | |
self: unsafe.Pointer(impl), // <- nil | |
} | |
} | |
// RealNil returns IfaceWrapper with all fields set to nil values (what nil does by it's definition) | |
func RealNil() IfaceWrapper { | |
return IfaceWrapper{ | |
vtable: nil, // <- nil | |
self: nil, // <- nil | |
} | |
} | |
// Here we can see that `ReturningNilUnsugared() != RealNil()`, so `ReturningNil() != nil` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment