Cannot use variable of type *T as type in argument - go

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{})
}

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()
}

Uninitialized Embedded Struct

I'm a bit perplexed by this go code. I have a struct (Outer) with an embedded struct (Inner), but when I initialize Outer, I intentionally leave the embedded struct uninitialized.
type Inner struct {
value int
}
func (i *Inner) MyFunc() string {
return "inner"
}
func (i *Inner) OnlyInner() string {
return "only inner stuff"
}
type Outer struct {
*Inner
}
func (o *Outer) MyFunc() string {
return "outer"
}
func main() {
// embedded struct is *not* initialized
o := &Outer{}
fmt.Println(o.Inner)
fmt.Println(o.Inner.MyFunc())
fmt.Println(o.Inner.OnlyInner())
//fmt.Println(o.Inner.value)
}
Output:
<nil>
inner
only inner stuff
And if I uncomment the last line (with o.Inner.value), I get a nil pointer dereference error.
What's up here? The effective go page says (https://golang.org/doc/effective_go.html#embedding):
When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one.
It seems like in my case, the inner type is <nil>, yet the method calls execute without problem. What's going on under the hood?
A method can be called with a nil receiver, as long as you do not dereference the receiver itself.
This means that the following works playground:
package main
import (
"fmt"
)
type foo struct {
val int
}
func (f *foo) Print() {
fmt.Println("Receiver:", f)
}
func (f *foo) PrintVal() {
fmt.Println("Val: ", f.val)
}
func main() {
var f *foo
f.Print()
//f.PrintVal()
}
f.Print() works without issues since we're just printing a pointer, we're not trying to dereference it.
However, f.PrintVal attempts to dereference a nil pointer, causing a panic.
When in doubt, remember that the methods in this example are equivalent to functions that take the receiver as first parameter:
func Print(f *foo)
func PrintVal(f *foo)
This is mentioned in the spec under method declarations:
The type of a method is the type of a function with the receiver as
first argument. For instance, the method Scale has type
func(p *Point, factor float64)
However, a function declared this way
is not a method.
This makes it clear that the receiver is nothing special, it can be nil as long as you don't dereference it.
The methods of the uninitialized struct are being called with a nil-receiver. If in the methods used that receiver you would get a panic. It is valid to call a method using a nil receiver, and the method could modify its behavior by checking if the receiver is nil.

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

Difference between method signatures of structs

As a programmer coming from other languages like C++, I find it rather strange that go allows to specify methods for structs that allow either a pointer or an instance as a parameter. According to go by example once could use either of them if we didn't want to modify the origin:
Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to avoid copying on method calls or to allow the method to mutate the receiving struct.
Consider the following code:
package main
import (
"fmt"
)
type Foo struct {}
type Bar struct {}
func (this Foo) String() string {
return "Foo"
}
func (this *Bar) String() string {
return "Bar"
}
func main() {
fmt.Println(Foo{}) // "Foo"
fmt.Println(Bar{}) // "{}"
}
Why can't I use both signature versions to modify the stringify (I don't know how it is actually called in go) behavior of the structs?
Just to be clear: I don't really care about the stringify, but want to understand how the language behaves.
Just add & to the Bar{} and make it pointer receiver, like this:
fmt.Println(&Bar{}) // "Bar"
Here a little adjustment to your code that outputs:
Foo
Bar
see:
package main
import "fmt"
type Foo struct{}
func (Foo) String() string {
return "Foo"
}
type Bar struct{}
func (*Bar) String() string {
return "Bar"
}
func main() {
fmt.Println(Foo{}) // "Foo"
pb := &Bar{}
fmt.Println(pb) // "Bar"
}
Notes:
Receiver name should be a reflection of its identity; don't use
generic names such as "this" or "self"
And you don't need names here for your example.
And nice to read Golang methods receivers:
Value receivers operate on a copy of the original type value. This
means that there is a cost involved, especially if the struct is very
large, and pointer received are more efficient.
Because Bar does not implement stringer *Bar does.
If you remove implementation of stringer from Foo, you will get "{}".
Similarly, When you write fmt.Println(Bar{}) it means it will look for something like func (Bar) String() and not func (*Bar) String()
Additioanlly , story is different when you write fmt.Println(&Foo{}), you might think it will print "{}" because there is no func (*Foo) String() but it will print "Foo".
For that, you will have to understand Interfaces. These are my experiences so please do your own research too. The fmt.Print function calls String() on passed arguments. So actually the String() is not called on your struct but rather than an variable of type stringer.
interface type can hold a type(which implemented it) or pointer to it,
if it was implemented with value receiver. That is why Foo{} and
&Foo{} both work.
interface type can hold a type's pointer (which implemented it)only,
if it was implemented with pointer receiver. Why? Because when you
implement an interface with pointer receiver, it needs an address
which can only be provided with a pointer. That is why only &Bar{}
works and not Bar{}

Why implicit non-pointer methods not satisfy interface?

Assuming we have an understanding that,
For explicit method definition for type X, GO compiler implicitly defines the same method for type *X and vice versa, if I declare,
func (c Cat) foo(){
//do stuff_
}
and declare,
func (c *Cat) foo(){
// do stuff_
}
then GO compiler gives error,
Compile error: method re-declared
which indicates that, pointer method is implicitly defined and vice versa
In the below code,
package main
type X interface{
foo();
bar();
}
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
func main() {
var c Cat
var p *Cat
var x X
x = p // OK; *Cat has explicit method bar() and implicit method foo()
x = c //compile error: Cat has explicit method foo() and implicit method bar()
}
GO compiler gives error,
cannot use c (type Cat) as type X in assignment:
Cat does not implement X (bar method has pointer receiver)
at x = c, because, implicit pointer methods satisfy interfaces, but implicit non-pointer methods do not.
Question:
Why implicit non-pointer methods do not satisfy interfaces?
Let's look into the language specification:
A type may have a method set associated with it. The method set
of an interface type is its interface. The method set of any other
type T consists of all methods declared with receiver type T. The
method set of the corresponding pointer type *T is the set of all
methods declared with receiver *T or T (that is, it also contains
the method set of T).
In your example, the method set of the interface type x is [foo(), bar()]. The method set of the type Cat is [foo()], and the method set of the type *Cat is [foo()] + [bar()] = [foo(), bar()].
This explains, why variable p satisfies the interface x, but variable c doesn't.
Method set
Following the spec:
The method set of any other named type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).
Method set definition sounds weird until you follow addressable and not addressable types concept.
Addressable and not addressable types
It is possible to call a pointer receiver method on a value if the value is of addressable type.
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.
It is ok to call pointer receiver methods on values till you are dealing with addressable types (struct is addressable):
type Cat struct {}
func (c *Cat) bar() string { return "Mew" }
func main() {
var c Cat
c.bar()
}
Variables of interface type are not addressable
But not all Go types are addressable. Also variables referenced through interfaces are not addressable.
It is impossible to call pointer receiver on values of not addressable types:
type X interface {
bar() string
}
type Cat struct{}
func (c *Cat) bar() string { return "Mew" }
/* Note `cat` variable is not a `struct` type value but
it is type of `X` interface therefor it is not addressable. */
func CatBar(cat X) {
fmt.Print(cat.bar())
}
func main() {
var c Cat
CatBar(c)
}
So with the following error Go runtime prevents segment fault:
cannot use c (type Cat) as type X in assignment:
Cat does not implement X (bar method has pointer receiver)
Add a little to dev.bmax's answer.
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
you can do
var c cat
c.bar() // ok to call bar(), since c is a variable.
but not
cat{}.bar() // not ok to call bar(), c is not a variable.
It's legal to call a *T method on an argument of type T so long as the argument is a variable; the compiler implicitly takes its address. But this is mere syntactic sugar: a value of type T does not posses all methods that a *T pointer does, and as a result it might satisfy fewer interfaces.
On the other hand, you can always call foo() with Cat or *Cat.
How about this?
package main
import (
"fmt"
)
type Growler interface{
Growl() bool
}
type Cat struct{
Name string
Age int
}
// *Cat is good for both objects and "references" (pointers to objects)
func (c *Cat) Speak() bool{
fmt.Println("Meow!")
return true
}
func (c *Cat) Growl() bool{
fmt.Println("Grrr!")
return true
}
func main() {
var felix Cat // is not a pointer
felix.Speak() // works :-)
felix.Growl() // works :-)
var ginger *Cat = new(Cat)
ginger.Speak() // works :-)
ginger.Growl() // works :-)
}

Resources