Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A method expression in Go is a mechanism that allows a method to be referenced as a standalone, unbound function by accessing it through its type rather than a specific instance. When a method is evaluated as an expression via its type, the compiler generates a standard function where the method’s receiver becomes the explicit first parameter of the resulting function signature.

Syntax and Signature Transformation

If a concrete type T has a method M with the signature func (t T) M(arg A) R, the method expression T.M yields a function with the signature func(t T, arg A) R. To invoke this function, you must explicitly pass an instance of T as the first argument.
package main

import "fmt"

type User struct {
    Name string
}

// Method with a value receiver
func (u User) Greet(greeting string) string {
    return greeting + ", " + u.Name
}

func main() {
    // Method Expression: accessed via the type 'User'
    // The signature transforms from func(string) string to func(User, string) string
    greetFunc := User.Greet 

    u := User{Name: "Alice"}
    
    // The receiver 'u' is passed explicitly as the first argument
    result := greetFunc(u, "Hello") 
    fmt.Println(result) // Output: Hello, Alice
}

Pointer vs. Value Receivers

The rules for method expressions strictly follow Go’s method set rules regarding pointer and value receivers. The type used in the method expression determines the type of the first parameter.
  1. Value Receiver Methods: Can be expressed using both the value type T and the pointer type *T.
  2. Pointer Receiver Methods: Can only be expressed using the pointer type *T.
package main

import "fmt"

type Counter struct {
    value int
}

// Pointer receiver
func (c *Counter) Increment(amount int) {
    c.value += amount
}

// Value receiver
func (c Counter) Value() int {
    return c.value
}

func main() {
    c := Counter{value: 10}

    // 1. Pointer receiver method expression
    // Signature: func(*Counter, int)
    incFunc := (*Counter).Increment 
    incFunc(&c, 5) // Invoked with pointer, c.value becomes 15
    
    // INVALID: Counter.Increment (Counter does not have Increment in its method set)

    // 2. Value receiver method expression via Value Type
    // Signature: func(Counter) int
    valFunc := Counter.Value 
    v1 := valFunc(c) 
    fmt.Println(v1) // Output: 15

    // 3. Value receiver method expression via Pointer Type
    // Signature: func(*Counter) int
    ptrValFunc := (*Counter).Value 
    v2 := ptrValFunc(&c)
    fmt.Println(v2) // Output: 15
}

Interface Type Method Expressions

Method expressions can also be derived from interface types. If I is an interface type containing a method M with the signature M(arg A) R, the method expression I.M yields a function with the signature func(i I, arg A) R. Unlike concrete type method expressions, invoking an interface method expression relies on dynamic dispatch. The concrete value passed as the explicit first argument determines which underlying method implementation is executed at runtime.
package main

import "fmt"

type Speaker interface {
    Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
    return "Woof"
}

type Cat struct{}

func (c Cat) Speak() string {
    return "Meow"
}

func main() {
    // Method Expression via Interface Type
    // Signature: func(Speaker) string
    speakFunc := Speaker.Speak

    var d Speaker = Dog{}
    var c Speaker = Cat{}

    // Dynamic dispatch based on the concrete value passed
    fmt.Println(speakFunc(d)) // Output: Woof
    fmt.Println(speakFunc(c)) // Output: Meow
}

Method Expression vs. Method Value

To understand method expressions mechanically, it is necessary to contrast them with method values:
  • Method Expression (Type.Method): Yields an unbound function. The receiver is not captured. The caller must provide the receiver as the explicit first argument during invocation.
  • Method Value (instance.Method): Yields a bound function (a closure). The specific instance is captured and implicitly passed to the method when invoked. The resulting function signature does not include the receiver.
package main

import "fmt"

type User struct {
    Name string
}

func (u User) Greet(greeting string) string {
    return greeting + ", " + u.Name
}

func main() {
    u := User{Name: "Bob"}

    // Method Expression (Unbound)
    expr := User.Greet
    res1 := expr(u, "Hi") // Requires 'u' as the first argument
    fmt.Println(res1)

    // Method Value (Bound)
    val := u.Greet
    res2 := val("Hi")     // 'u' is already captured; only takes the 'greeting' argument
    fmt.Println(res2)
}
Master Go with Deep Grasping Methodology!Learn More