tags | |
---|---|
|
For a type T
, T.M
is a function that is callable as a regular function with the same arguments as M
prefixed by an additional argument that is the receiver of the method
For example, given:
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
T.Mv
yields a function with the signaturefunc(tv T, a int) int
(*T).Mp
yields a function with the signaturefunc(tp *T, f float32) float32
(*T).Mv
yields a function with the signaturefunc(tv *T, a int) int
. This function indirects through the receiver to create a value to pass as the receiver to the underlying methodT.Mp
is illegal, because pointer-receiver methods are not in the method set of the value type
It is legal to derive a function value from a method of an interface type. The resulting function takes an explicit receiver of that interface type
If x
has type T
, the method value x.M
is a function value that is callable with the same arguments as a method call of x.M
The expression x
is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later
For example, given:
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t T
var pt *T
t.Mv
yields a function value of typefunc(int) int
pt.Mp
yields a function value of typefunc(float32) float32
pt.Mv
is equivalent to(*pt).Mv
t.Mp
is equivalent to(&t).Mp
ift
is addressable
Closures may refer to variables defined in a surrounding function (free variables). Those variables are then shared between the surrounding function and the closure, and they survive as long as they are accessible
Closed over variables semantically are always captured by address, meaning they escape to the heap
However, internally, a closure can be passed a copy of a value (not the address) to reduce GC pressure. For example, if the value is a constant or is never modified
A compiler rewrites a direct call of a function literal into a normal function call with closure variables passed as arguments. This avoids allocation of a closure object
For example, this call:
func(a int) {
println(byval)
byref++
}(42)
becomes:
func(byval int, byref *int, a int) {
println(byval)
(*byref)++
}(byval, byref, 42)
Function values are pointers that point to structs. These structs contain either:
- Just the pointer to the function code for simple functions
- Pointers to an autogenerated wrapper function and receivers and/or function parameters in the case of method calls and closures
For example, this closure:
func closure() func() *byte {
var b [4096]byte
return func() *byte {
return &b[0]
}
}
becomes:
type closure_1_obj struct {
F uintptr
b *[4096]byte
}
func closure_1(p *closure_1_obj) *byte {
return &p.b[0]
}
func closure() *struct{ F uintptr } {
var b [4096]byte
p := &closure_1_obj{
F: &closure_1,
b: &b,
}
return (*struct{ F uintptr })(unsafe.Pointer(p))
}
- Go internals: capturing loop variables in closures - Eli Bendersky's website
- Go internals: capturing loop variables in closures : r/golang
- How Golang Closures are layed out in memroy? : r/golang
- How are Go closures layed out in memory? - Stack Overflow
- go/src/cmd/compile/internal/walk/closure.go at master · golang/go · GitHub
- The Go Programming Language Specification - The Go Programming Language
- What is a Go function variable? · Phil Pearl's Blog
- Methods in Go - Go 101