Unsafe pointers, emptyInterface word - go

I am trying to understand Go unsafe.Pointers internal. In case when there is only one field in the struct and returns me a pointer to the Foo, returns a pointer to the Bar.
Is there a way to actually return allways pointer to the Foo not Bar? It works only when *Foo or Foo has more than one field. Is it compiler optimization, or is it related with Heap/Stack allocation?
type Bar struct {
ID int
Name string
Price float64
}
type Foo struct {
Bar *Bar
}
aFoo := Foo{
Bar: &Bar{},
}
//xunsafe.AsPointer implementation:
//
// func AsPointer(v interface{}) unsafe.Pointer {
// empty := (*emptyInterface)(unsafe.Pointer(&v))
// return empty.word
//}
fmt.Printf("Foo ptr: %v, Bar ptr: %v\n", AsPointer(aFoo), unsafe.Pointer(aFoo.Bar))
//Output: Foo ptr: 0xc00010a060, Bar ptr: 0xc00010a060
Edit: I am using the go1.17.

Use &aFoo to get a pointer to the Foo.
The type unsafe.Pointer is a special type understood by the compiler. The compiler allows conversions between unsafe.Pointer and other types that are not normally allowed by the language specification.
Regarding xunsafe.Pointer: You see different results for
type Foo struct {
Bar *Bar
}
and
type Foo struct {
Bar *Bar
ASecondField int
}
because those types have different representations when stored in an interface value. Values larger than a word are copied to memory private to the interface value and the interface value stores a pointer to that memory. The xunsafe.AsPointer function does have code to handle both of representations.

Related

Confused by generics - pointer type parameter?

I'm confused by generics. I've read a bunch of answers to similar questions and I'm still stumped.
I get that the answer here involves telling the compiler that T and *T are related. That while the implicit type conversion from one to the other happens normally in straight code, that doesn't happen in generics without more code. Could someone please walk me through this?
package main
type Mungeable interface {
Munge() // a Mungeable is a thing that can be Munge'd
}
type Box[T Mungeable] struct {
Contents *T // A box is a type based on a Mungeable
}
type Foo struct{} // Foo is a struct...
func (f *Foo) Munge() {} // ...that can be munged.
func (b *Box[Foo]) Print() {
_ = b.Contents.Munge()
}
produces:
./prog.go:16:17: b.Contents.Munge undefined (type *Foo is pointer to type parameter, not type parameter)
Go build failed.
what do I need to change to get this to compile?
playground here: https://go.dev/play/p/gNqxWhlmWmz
Mungable is an interface, a pointer to an interface has no method-set (unlike a pointer to concrete type, which has a method-set containing the methods of its base type in addition to whatever methods were declared for the pointer type), so you need to dereference the pointer before calling the interface's method.
Another issue is that Munge does not have a return value, therefore the assignment statement _ = ...Munge() will cause a compile time error.
The following works:
package main
type Mungeable interface {
Munge() // a Mungeable is a thing that can be Munge'd
}
type Box[T Mungeable] struct {
Contents *T // A box is a type based on a Mungeable
}
type Foo struct{} // Foo is a struct...
func (f *Foo) Munge() {} // ...that can be munged.
func (b *Box[Foo]) Print() {
(*b.Contents).Munge()
}
func main() {}
https://go.dev/play/p/HWW6k47Cdvh
Your understanding of methods and interfaces is wrong. The method set of Foo includes those that defined with the receiver of type Foo, while the method set of *Foo includes methods defined with the receiver of both types Foo and *Foo. Given these declarations
type Mungeable interface {
Munge()
}
type Foo struct {}
func (f *Foo) Munge() {}
Then Foo does not implement Mungeable, it is *Foo that does so. This is reflected in the fact that you cannot assign a value of type Foo to a variable of type Mungeable, and var x Mungeable = new(Foo) is fine. On the contrary, if your Munge method is defined as so
func (f Foo) Munge() {}
Then both Foo and *Foo would implement Mungeable.
The second mistake of yours is that func (f *Box[Foo]) Print() does not define a method with the receiver of type Box[Foo], it defines a method with the receiver of generics type Box with the type parameter declared as Foo. In other words, it is the same as func (f *Box[T]) Print(). If you want to act on Box[Foo] only, then I think you must put it in the parameter list, as I don't think Go allows specialisation of generics classes.
The solution to your problem is that you can just make Contents of type T and change type parameter to *Foo, like this:
package main
type Mungeable interface {
Munge() // a Mungeable is a thing that can be Munge'd
}
type Box[T Mungeable] struct {
Contents T // A box is a type based on a Mungeable
}
type Foo struct{} // Foo is a struct...
func (f *Foo) Munge() {} // ...that can be munged.
func Print(b *Box[*Foo]) {
b.Contents.Munge()
}

Cannot use variable of type *T as type in argument

I'm learning Go 1.18 generics and I'm trying to understand why I'm having trouble here. Long story short, I'm trying to Unmarshal a protobuf and I want the parameter type in blah to "just work". I've simplified the problem as best I could, and this particular code is reproducing the same error message I'm seeing:
./prog.go:31:5: cannot use t (variable of type *T) as type stringer in argument to do:
*T does not implement stringer (type *T is pointer to type parameter, not type parameter)
package main
import "fmt"
type stringer interface {
a() string
}
type foo struct{}
func (f *foo) a() string {
return "foo"
}
type bar struct{}
func (b *bar) a() string {
return "bar"
}
type FooBar interface {
foo | bar
}
func do(s stringer) {
fmt.Println(s.a())
}
func blah[T FooBar]() {
t := &T{}
do(t)
}
func main() {
blah[foo]()
}
I realize that I can completely simplify this example by not using generics (i.e., pass the instance to blah(s stringer) {do(s)}. However, I do want to understand why the error is happening.
What do I need to change with this code so that I can create an instance of T and pass that pointer to a function expecting a particular method signature?
In your code there's no relationship between the constraints FooBar and stringer. Furthermore the methods are implemented on the pointer receivers.
A quick and dirty fix for your contrived program is simply to assert that *T is indeed a stringer:
func blah[T FooBar]() {
t := new(T)
do(any(t).(stringer))
}
Playground: https://go.dev/play/p/zmVX56T9LZx
But this forgoes type safety, and could panic at run time. To preserve compile time type safety, another solution that somewhat preserves your programs' semantics would be this:
type FooBar[T foo | bar] interface {
*T
stringer
}
func blah[T foo | bar, U FooBar[T]]() {
var t T
do(U(&t))
}
So what's going on here?
First, the relationship between a type parameter and its constraint is not identity: T is not FooBar. You cannot use T like it was FooBar, therefore *T is definitely not equivalent to *foo or *bar.
So when you call do(t), you're attempting to pass a type *T into something that expects a stringer, but T, pointer or not, just does not inherently have the a() string method in its type set.
Step 1: add the method a() string into the FooBar interface (by embedding stringer):
type FooBar interface {
foo | bar
stringer
}
But that's not enough yet, because now none of your types actually implement it. Both declare the method on the pointer receiver.
Step 2: change the types in the union to be pointers:
type FooBar interface {
*foo | *bar
stringer
}
This constraint now works, but you have another problem. You can't declare composite literals when the constraint doesn't have a core type. So t := T{} is also invalid. We change it to:
func blah[T FooBar]() {
var t T // already pointer type
do(t)
}
Now this compiles, but t is actually the zero value of a pointer type, so it's nil. Your program doesn't crash because the methods just return some string literal.
If you need to also initialize the memory referenced by the pointers, inside blah you need to know about the base types.
Step 3: So you add T foo | bar as one type param, and change the signature to:
func blah[T foo | bar, U FooBar]() {
var t T
do(U(&t))
}
Done? Not yet. The conversion U(&t) is still invalid because the type set of both U and T don't match. You need to now parametrize FooBar in T.
Step 4: basically you extract FooBar's union into a type param, so that at compile time its type set will include only one of the two types:
type FooBar[T foo | bar] interface {
*T
stringer
}
The constraint now can be instantiated with T foo | bar, preserve type safety, pointer semantics and initialize T to non-nil.
func (f *foo) a() string {
fmt.Println("foo nil:", f == nil)
return "foo"
}
func main() {
blah[foo]()
}
Prints:
foo nil: false
foo
Playground: https://go.dev/play/p/src2sDSwe5H
If you can instantiate blah with pointer types, or even better pass arguments to it, you can remove all the intermediate trickery:
type FooBar interface {
*foo | *bar
stringer
}
func blah[T FooBar](t T) {
do(t)
}
func main() {
blah(&foo{})
}

Avoid same address for empty structs

In an attempt to have more useful enums in Go, I've tried using struct pointers as enum values. This works great if the struct has fields, but for the empty struct I sometimes get the same pointer multiple times, regardless of whether I use &Foo{} or new(Foo).
Is there any way I can force the pointers to be different?
This is what I'm trying to do (try it online):
package main
type Foo struct{}
var FooValues = struct {
Alpha *Foo
Bravo *Foo
Charlie *Foo
}{
Alpha: &Foo{}, // These pointers should all be different
Bravo: &Foo{}, // But sometimes
Charlie: &Foo{}, // They are not
}
func main() {
if FooValues.Alpha == FooValues.Charlie {
panic("enum values are the same!")
}
}
Is there any way I can force the pointers to be different?
No. Any implementation is allowed to use the same address. You must redesign.
From the Go language specification:
Pointers to distinct zero-size variables may or may not be equal.
This seems to do it:
package main
type foo struct{}
func newFoo() *foo {
return new(foo)
}
type fooValues struct { alpha, bravo, charlie *foo }
func newFooValues() fooValues {
return fooValues{
newFoo(), newFoo(), newFoo(),
}
}
func main() {
f := newFooValues()
if f.alpha == f.charlie {
panic("enum values are the same!")
}
}
As mentioned, pointer comparison for zero-sized types is not reliable in this way.
Replace
type Foo struct{}
with
type Foo struct{int}
Now your code works. The added cost is ~1 word of memory per enum that you allocate, which should be negligible in most cases.

How to assign a pointer to interface in Go

package main
import "fmt"
type intr interface {
String() string
}
type bar struct{}
func (b *bar) String() string {
return "bar"
}
type foo struct {
bar *intr
}
func main() {
bar1 := bar{}
foo1 := foo{bar: &bar1}
fmt.Println(foo1)
}
I get a compile-time error:
cannot use &bar1 (type *bar) as type *intr in field value: *intr is pointer to interface, not interface
Why is this error happened?
How to assign foo.bar?
You're assigning it to a pointer to the interface. After changing the field type to interface it will work:
type foo struct {
bar intr
}
Pointers to interfaces are quite rarely needed.
Uber-go style guide pointers-to-interfaces contains exact answer to your question,
You almost never need a pointer to an interface. You should be passing interfaces as values—the underlying data can still be a pointer. An interface is two fields: A pointer to some type-specific information. You can think of this as "type."
And a data pointer. If the data stored is a pointer, it’s stored directly. If the data stored is a value, then a pointer to the value is stored.
If you want interface methods to modify the underlying data, you must use a pointer.
My recommendation is to get acquainted with it as soon as possible,
Hope it helps

Embedding struct vs pointer to struct in struct used as pointer

If I have a struct type A which is used as a pointer (has pointer receivers only, the constructor returns *A, etc.), what is the difference between embedding a struct type B as B versus *B?
That is, what is the difference between
type B struct {...}
type A struct {
B
// ...
}
and
type B struct {...}
type A struct {
*B
// ...
}
For example, is there ever copying of the embedded field?
Edit: I should also mention that the embedded struct B only has pointer receivers.
The zero values of the two structures are different, which can be a significant ergonomic difference.
Consider an embedded type
type B struct {
X int
}
func (b *B) Print() { fmt.Printf("%d\n", b.X) }
If we embed this directly as an object
type AObj struct {
B
}
then the zero value of type AObj includes an embedded object of type B, which also has its zero value, and therefore we can safely
var aObj AObj
aObj.Print() // prints 0
But if we instead embed a pointer
type APtr struct {
*B
}
the zero value of this struct has a nil pointer value, and we can't really use it directly.
var aPtr APtr
aPtr.Print() // panics
Objects get copied in hopefully the way you might expect. If you create a new AObj object, it gets a copy of the embedded B.
aObj2 := aObj
aObj.X = 1
aObj2.Print() // prints 0, because it has a copy
If you create a new APtr object, it gets a copy of the *B, which means it shares the underlying concrete object.
aPtr.B = &B{}
aPtr2 := aPtr
aPtr.X = 1
aPtr2.Print() // prints 1, because both objects point at the same B
Runnable example at https://play.golang.org/p/XmOgegwVFeE
Consider a simple example program. A structAPtr embeds a pointer, a structAVal embeds a struct structB directly:
package main
import "fmt"
type structB struct {
foo int
}
type structAPtr struct {
bar *structB
}
type structAVal struct {
bar structB
}
func main() {
// referencing bStruct
b1 := structB{foo: 12}
aPtr := structAPtr{bar: &b1}
fmt.Println("Before assignment:")
fmt.Printf("aPtr.bar.foo = %d, b.foo = %d\n", aPtr.bar.foo, b1.foo)
aPtr.bar.foo = 42
fmt.Println("After assignment:")
fmt.Printf("aPtr.bar.foo = %d, b.foo = %d\n", aPtr.bar.foo, b1.foo)
// copying bStruct
b2 := structB{foo: 12}
aVal := structAVal{bar: b2}
fmt.Println("Before assignment:")
fmt.Printf("aVal.bar.foo = %d, b.foo = %d\n", aVal.bar.foo, b2.foo)
aVal.bar.foo = 42
fmt.Println("After assignment:")
fmt.Printf("aVal.bar.foo = %d, b.foo = %d\n", aVal.bar.foo, b2.foo)
}
The int structB.foo is used to demonstrate whether structB changes when manipulated inside of structAPtr or structAVal.
This program outputs:
Before assignment:
aPtr.bar.foo = 12, b.foo = 12
After assignment:
aPtr.bar.foo = 42, b.foo = 42 <------------ both changed
Before assignment:
aVal.bar.foo = 12, b.foo = 12
After assignment:
aVal.bar.foo = 42, b.foo = 12 <------------ only assignee changed
Looking at the result shows:
changing the value of the pointer to structB changes structB
changing the value of the copied version structB in structAVal leaves structB unaffected (it is still 5, even after 42 was assigned to aVal)
Edit:
If your structB has only pointer receivers anyways, the intended behavior is probably such that changing structB in strucA updated both of them. That's scenario 1 in my example and requires a pointer for sure. From A Tour of Go:
Methods with pointer receivers can modify the value to which the receiver points [...]. Since methods often need to modify their receiver, pointer receivers are more common than value receivers.
Hope that helps!

Resources