Method receivers ambiguity - go

Working on the go tour today. I noticed that I could pass struct literals to methods associated with pointer to structs, and vice versa. Why is this allowed?
package main
import (
"fmt"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Scale (f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) ScaleP(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := &Vertex{3, 4}
vLiteral := Vertex{3, 4}
v.Scale(5)
fmt.Println(v)
v.ScaleP(5)
fmt.Println(v)
vLiteral.Scale(5)
fmt.Println(vLiteral)
vLiteral.ScaleP(5)
fmt.Println(vLiteral)
}
Output:
&{3 4}
&{15 20}
{3 4}
{15 20}

See Method sets:
A type may have a method set associated with it (§Interface types, §Method declarations). The method set of an interface type is its interface. The method set of any other 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). Further rules apply to structs containing anonymous fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique method name.
The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.
EDIT:
See also Calls:
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m():

Related

Type definition - cannot use basic type as custom type even though basic type is source of custom type

In the below code, we are using type definition syntax in Line 5,6,7:
package math
// Constructors and Selectors
type RationalNumber []int // Line 5
type Numerator int
type Denominator int
// Constructor - Construct a rational number x that represents n/d
func NewRationalNumber(n int, d int) RationalNumber {
g := gcd(n, d)
return []int{n / g, d / g} // Line 12
}
//Selector
func numer(x RationalNumber) Numerator {
return x[0] // Line 17
}
//Selector
func denom(x RationalNumber) Denominator {
return x[1]
}
return []int{n / g, d / g} does not give error,
where as,
return x[0] & return x[1] give error:
math/numbers.go:17:10: cannot use x[0] (type int) as type Numerator in return argument
math/numbers.go:22:10: cannot use x[1] (type int) as type Denominator in return argument
For given 3 type definitions in Line 5,6,7, I understand the reason behind error on Line 17, but,
Why Line 12 does not give similar error?
How to resolve this error? without changing the signature of functions?
The defined type RationalNumber was created from a slice with elements of type int, therefore subscription on an instance of the defined type gives an int, not any of the newly defined types created from int.
The compiler does not complain about line 12 since the syntax is the slice literal appropriate for creating the defined type (hint: elements of new slice type are int).
You are returning a type different from what was specified in the function definitions.
Take numer(x RationalNumber) Numerator for example
numer(x RationalNumber) Numerator accepts a variable of type RationalNumber and is expected to return a variable of type Numerator.
The problem is that you are returning a type int (as RationalNumber is a slice of ints) but a type Numerator is expected.
You can refactor your code to cast the int returned by RationalNumber to the appropriate types.
type RationalNumber []int
type Numerator int
type Denominator int
func NewRationalNumber(n int, d int) RationalNumber {
g := gcd(n, d)
return []int{n / g, d / g}
}
func numer(x RationalNumber) Numerator {
return Numerator(x[0])
}
func denom(x RationalNumber) Denominator {
return Denominator(x[1])
}
I know that, my question is why there is no necessity to do RationalNumber([]int{n / g, d / g})?
You do not need to do so because []int{n/g, d/g} is already a slice of ints.
type Numerator = int syntax is resolving the error. Am not sure, how type Numerator = int different from type Numerator int? No need to Numerator(x[0])
type Numerator = int is a type alias for int. This does not create a new type that is distinct from int.
Whereas type Numerator int creates a new custom type called Numerator which has a source type of int. Numerator can be cast to int but it is distinct from int
You can refer to Type definitions vs Type Alias for more explanations
This question bothered me as well, so here is answer that i came up with.
Take your function as an example:
func NewRationalNumber(n int, d int) RationalNumber {
g := gcd(n, d)
return []int{n / g, d / g}
}
According to function signature, it should return RationalNumber type, but in fact it returns int slice. Reason why it works is that type []int is assignable to type RationalNumber. Specifically it satisfies second condition, mentioned in language spec:
x's type V and T have identical underlying types and at least one of V or T is not a defined type.
This condition is satisfied, because in that case, we have only one defined type(RationalNumber) and another one is composite type([]int).
So, why do we get error when we define following function?:
func numer(x RationalNumber) Numerator {
return x[0]
}
Type Numerator has int as underlying type and we return int, which is predeclared type, so it should work, right? No) Thing is, although int is predeclared, it's still defined type. As it's stated in language spec:
To avoid portability issues all numeric types are defined types
In fact, all predeclared types, except error, are defined types.

What's the reason for having methods outside the definition of the struct?

Why do we have the methods declared outside the type definition of the struct? E.g.:
type antenna struct {
name string
length float32
girth float32
bloodtype string
}
func (p *antenna) extend() {
p.length += 10
}
It seems to me that the method could be part of the struct? (Let's ignore for now that structs are supposed to be value types)
type antenna struct {
name string
length float32
girth float32
bloodtype string
func extend() {
length += 10
}
}
This would be more similar to traditional OOP. I didn't find any good explanations of why it is done the way it is besides "structs are value-types and classes are reference-types". I know the difference, but it's not a satisfactory answer to me. In any way the method has to be called like this:
var x = antenna()
x.extend()
So what's the point of separating the the struct and methods? Having them visually grouped together in the code - as in typical OOP languages - seems useful to me?
TLR: Code reuse, and Consistency.
1 - This enables to reuse methods:
This is the key design principle of the interface type in Go - let me make it more clear with an example: Consider you need to sort an slice of int (try it here):
a := []int{1, 3, 2, 5, 4}
sort.Ints(a) // sort.Sort(sort.IntSlice(a))
fmt.Println(a) // [1 2 3 4 5]
You simply call sort.Ints(a) which then calls Sort(IntSlice(a)) inside the standard library:
type IntSlice []int
func (x IntSlice) Len() int { return len(x) }
func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
sort.IntSlice attaches the 3 methods of sort.Interface: Len, Less, and Swap to the type []int, to call:
// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
So you are able to reuse methods from the standard library, and you don't need to reimplement it again.
2- You may define your own types, See this example - There is no inside here for this named type - so methods must be outside of this type:
package main
import "fmt"
type num int32
func (p *num) inc() {
*p++
}
func main() {
p := num(100)
p.inc()
fmt.Println(p) // 101
}
The above named type num versus this user defined type: By design this makes the Go language consistent for both types:
type Animal struct {
Name string
moves []move.Direction
}
func (p *Animal) Walk(dir move.Direction) {
p.moves = append(p.moves, dir)
}
See also:
In Go is naming the receiver variable 'self' misleading or good practice?

About method implementation for an interface

Can anyone explain why the calling of a.Abs() works?
In my opinion, 'a' is a variable of type *Vertex, but the type *Vertex doesn't implement the method Abs.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
v := Vertex{3, 4}
a = &v // a *Vertex implements Abser
// In the following line, v is a *Vertex (not Vertex)
// and does NOT implement Abser, but why does this calling work?
fmt.Println(a.Abs())
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
From the fine specification:
Method sets
[...] 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). [...]
Your Abs function is in the method sets of both Vertex and *Vertex so *Vertex is an Abser just like Vertex is.
Other related sections:
Method values
Selectors
In general, pointers are automatically dereferenced when possible so you can say x.M() and x.V without worrying about whether or not x is a pointer and there is no need for C's -> or manual dereferencing (i.e. (*x).M() or (*x).V).

Methods with a Pointer Receiver

I am newbie in computer science.I just read the code snippet below from The Go programming language and got the following error log.
func (p *Point) ScaleBy(factor float64){
p.X *= 2
p.Y *= 2
}
Point{1, 2}.ScaleBy(2)
# error log
cannot call pointer method on point literal
cannot take the address of point literal
point literal.scaleby(2) used as value
The book explained that we can not call a *Point method on a non-addressable Point receiver, because there's no way to obtain address of a temporary value.
However, if I print &Point{1, 2}, this would not throw error. Accordingly, why Point{1,2} is a non-addressable Point receiver?
By using Point{1, 2}.ScaleBy(2) you are trying to call pointer receiver method ScaleBy with value: Point{1, 2}:
The method set of any other type T consists of all methods declared
with receiver type T.
but if you use addressable type:
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).
then it is possible: meaning you or the compiler should get the address of temporary value (Taking the address of a composite literal):
Address operators:
For an operand x of type T, the address operation &x generates a
pointer of type *T to x. The operand must be addressable, that is,
either a variable, pointer indirection, or slice indexing operation;
or a field selector of an addressable struct operand; or an array
indexing operation of an addressable array. As an exception to the
addressability requirement, x may also be a (possibly parenthesized)
composite literal. If the evaluation of x would cause a run-time
panic, then the evaluation of &x does too.
ref: https://golang.org/ref/spec#Address_operators
You may call (&Point{1, 2}).ScaleBy(2)
like this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := (&Point{1, 2}).ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return p
}
you may call Point{1, 2}.ScaleBy(2)
like this working sample code (value receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}.ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return &p
}
output:
&{2 4}
also see this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}
p.ScaleBy(2)
fmt.Println(p) // {2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) {
p.X *= 2
p.Y *= 2
}
output:
{2 4}
When you write Point{1,2} you simply declaring and initializing a value of type Point. If you don't assign it to a variable, it is discarded.
Go disallows this behavior of calling a pointer method on a simple value since a pointer method states an intent of object (pointed to by the pointer) modification. A pointer method called with a value would be useless in most of the cases since a value is passed by copy to the method. Any modifications made to the value will be done to that copied value and no actual modification would occur.
If you tried this, it would work:
type Point struct {
x, y int
}
func (p Point) X() {
fmt.Println(p.x)
}
Point{1, 2}.X() // 1
You can read more about it here: https://golang.org/doc/effective_go.html#pointers_vs_values

Implementing Mixins and an Inconsistency in Compiler Behavior

Mixins can be implemented in Go (1.4.1) using embedding and since struct{} occupies no memory (as I understand) it fits for the situations that we want to add some functionality or just add a method to a type that may actually has nothing to do with it's state, but we like to avoid ParseThing(...) and instead write thing.Parse(...).
So having:
type X struct{}
func (x X) F() {
fmt.Println("functionality in X.F()")
}
type Y struct{ X }
type Z struct{ Y }
Then if we do:
var z Z
z.F()
Will give us:
functionality in X.F()
So far so good.
Now let's add another type OX with method F() and embed it in Z:
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x OX) F() {
fmt.Println("functionality in OX.F()")
}
Interesting! Now we get functionality in OX.F() which shows us that Go compiler searches for the method, starting from type it self and then the last embedded type. We can check that by adding F() to Z:
func (x Z) F() {
fmt.Println("functionality in Z.F()")
}
The output is functionality in Z.F(). Now if we remove the Z.F() method and add F() to Y:
//func (x Z) F() {
// fmt.Println("functionality in Z.F()")
//}
func (x Y) F() {
fmt.Println("functionality in Y.F()")
}
Then we see this error ambiguous selector z.F; redirecting via pointers makes no difference.
Question 1: Why that's so?
The extra level of indirection Y meant for something else, but brought me to this. And as I've guessed func (t T) String() string{} is an exception. This code:
type X struct{}
func (x X) String() string {
return "in X.String()"
}
type Y struct{ X }
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x OX) String() string {
return "in OX.String()"
}
func (x Y) String() string {
return "in Y.String()"
}
And then this:
var z Z
fmt.Println(z)
Gives us:
{in Y.String() in OX.String()}
Which is logical. But if we use pointer receivers:
import (
"fmt"
"testing"
)
func TestIt(t *testing.T) {
var z Z
fmt.Println(z)
}
type X struct{}
func (x *X) String() string {
return "in X.String()"
}
type Y struct{ X }
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x *OX) String() string {
return "in OX.String()"
}
func (x *Y) String() string {
return "in Y.String()"
}
Will print out:
{{{}} {}}
Question 2: Why is that so?
Question 1
The compiler is correct. How should it decide, which of OX.F and Y.F should it use? It can't. So it's up to you to call the desired method directly: either with
z.Y.F()
or
z.OX.F()
Edit: As for why your example worked until you've defined F on Y, this is mentioned in the Spec:
For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
(Emphasis added.)
Before you defined the method, the shallowest implementation was OX.F. After you've defined Y.F, there became two Fs on the same level, which is illegal.
Question 2
Again, the compiler is correct. You have embedded types Y and OX into Z, not *Y and *OX. As written in the Spec,
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).
*T has all methods of T, but not the other way around. Methods sets of OX and Y are empty, so obviously, fmt.Println just prints them as if they were any other kind of struct with no String() method defined.
Ainar-G write neat answer
Spec:
For a value x of type T or *T where T is not a pointer or interface
type, x.f denotes the field or method at the shallowest depth in T
where there is such an f. If there is not exactly one f with
shallowest depth, the selector expression is illegal.
I'd like to add a bit
Spec:
If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
So things would work if you just use referencing to promote methods like
fmt.Println(&z)
But this will cause ambiguity in selection cause of there are few possibilities for String method and so selector String is illegal due to spec. Compiler must complain, but it doesn't. This behaviour looks unspecified and can be only explained as special case for common printing operation to my mind.
This will work as expected
var y Y
fmt.Println(&y)
Here is working example
Playground

Resources