It's worth noting that the words "stack" and "heap" do not appear anywhere in the language spec. Your question is worded with "...is declared on the stack," and "...declared on the heap," but note that Go declaration syntax says nothing about stack or heap.
That technically makes the answer to all of your questions implementation dependent. In actuality of course, there is a stack (per goroutine!) and a heap and some things go on the stack and some on the heap. In some cases the compiler follows rigid rules (like "new
always allocates on the heap") and in others the compiler does "escape analysis" to decide if an object can live on the stack or if it must be allocated on the heap.
In your example 2, escape analysis would show the pointer to the struct escaping and so the compiler would have to allocate the struct. I think the current implementation of Go follows a rigid rule in this case however, which is that if the address is taken of any part of a struct, the struct goes on the heap.
For question 3, we risk getting confused about terminology. Everything in Go is passed by value, there is no pass by reference. Here you are returning a pointer value. What's the point of pointers? Consider the following modification of your example:
type MyStructType struct{}
func myFunction1() (*MyStructType, error) {
var chunk *MyStructType = new(MyStructType)
// ...
return chunk, nil
}
func myFunction2() (MyStructType, error) {
var chunk MyStructType
// ...
return chunk, nil
}
type bigStruct struct {
lots [1e6]float64
}
func myFunction3() (bigStruct, error) {
var chunk bigStruct
// ...
return chunk, nil
}
I modified myFunction2 to return the struct rather than the address of the struct. Compare the assembly output of myFunction1 and myFunction2 now,
--- prog list "myFunction1" ---
0000 (s.go:5) TEXT myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL ,runtime.new+0(SB)
0003 (s.go:6) MOVQ 8(SP),AX
0004 (s.go:8) MOVQ AX,.noname+0(FP)
0005 (s.go:8) MOVQ $0,.noname+8(FP)
0006 (s.go:8) MOVQ $0,.noname+16(FP)
0007 (s.go:8) RET ,
--- prog list "myFunction2" ---
0008 (s.go:11) TEXT myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ chunk+0(SP),DI
0010 (s.go:12) MOVQ $0,AX
0011 (s.go:14) LEAQ .noname+0(FP),BX
0012 (s.go:14) LEAQ chunk+0(SP),BX
0013 (s.go:14) MOVQ $0,.noname+0(FP)
0014 (s.go:14) MOVQ $0,.noname+8(FP)
0015 (s.go:14) RET ,
Don't worry that myFunction1 output here is different than in peterSO's (excellent) answer. We're obviously running different compilers. Otherwise, see that I modfied myFunction2 to return myStructType rather than *myStructType. The call to runtime.new is gone, which in some cases would be a good thing. Hold on though, here's myFunction3,
--- prog list "myFunction3" ---
0016 (s.go:21) TEXT myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ $0,AX
0019 (s.go:22) MOVQ $1000000,CX
0020 (s.go:22) REP ,
0021 (s.go:22) STOSQ ,
0022 (s.go:24) LEAQ chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ .noname+0(FP),DI
0024 (s.go:24) MOVQ $1000000,CX
0025 (s.go:24) REP ,
0026 (s.go:24) MOVSQ ,
0027 (s.go:24) MOVQ $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ $0,.noname+8000008(FP)
0029 (s.go:24) RET ,
Still no call to runtime.new, and yes it really works to return an 8MB object by value. It works, but you usually wouldn't want to. The point of a pointer here would be to avoid pushing around 8MB objects.
First of all, type assertion can be used only on interfaces:
For an expression x
of interface type and a type T
, the primary expression
x.(T)
asserts that x
is not nil and that the value stored in x
is of type T
. The notation x.(T)
is called a type assertion.
But you're applying it to non interface typed fields (int
and string
). That makes compiler unhappy.
Secondly, if you want to return type T
from a method/function, it's always enough to return an expression of type T
, which your fields already happen to be. The correct code is then easy:
package main
import "fmt"
type MyData struct {
field1 string
field2 int
}
func (a MyData) OperatorOnString() string {
return a.field1
}
func (a MyData) OperatorOnInt() int {
return a.field2
}
func main() {
a := MyData{"foo", 42}
fmt.Println(a.OperatorOnString(), a.OperatorOnInt())
}
Playground
Output:
foo 42
Best Answer
Short answer
In one line:
Why would I use them:
x
is nilWhat exactly they return:
t := x.(T)
=> t is of typeT
; ifx
is nil, it panics.t,ok := x.(T)
=> ifx
is nil or not of typeT
=>ok
isfalse
otherwiseok
istrue
andt
is of typeT
.Detailed explanation
Imagine you need to calculate area of 4 different shapes: Circle, Square, Rectangle and Triangle. You may define new types with a new method called
Area()
, like this:And for
Triangle
:And for
Rectangle
:And for
Square
:Here you have
Circle
, with radius of 1.0, and other shapes with their sides:Interesting! How can we collect them all in one place?
First you need
Shape interface
to collect them all in one slice of shape[]Shape
:Now you can collect them like this:
After all,
Circle
is aShape
andTriangle
is aShape
too.Now you can print the area of each shape using the single statement
v.Area()
:So
Area()
is a common interface between all shapes. Now, how can we calculate and call uncommon method like angles of triangle using aboveshapes
?Now it's time to extract
Triangle
from aboveshapes
:Using
t, ok := v.(Triangle)
we requested type assertions, meaning we asked the compiler to try to convertv
of typeShape
to typeTriangle
, so that if it's successful, theok
will betrue
otherwisefalse
, and then if it is successful callt.Angles()
to calculate the triangle's three angles.This is the output:
And the whole working sample code:
Also see:
Type assertions
EDIT
Question: What does the assertion
x.(T)
return when T is aninterface{}
and not a concrete type?Answer:
E.g. this panics (compile: Success, Run:
panic: interface conversion: interface is nil, not interface {}
):And this works (Run: OK):
Output:
NOTE: here
c
is of typeinterface {}
and notint
.See this working sample code with commented outputs: