Golang func pointer of a struct member

14,149

Solution 1

You can even do it directly, without an interface:

package main

import "fmt"

type A struct {
    Name string
}

func (a *A) Go() {
    fmt.Printf("GO: %v\n", a)
}

func Test(fn func()) {
    fn()
}

func main() {
    aa := &A{Name: "FOO"}
    bb := (*A)(nil)
    cc := &A{}
    Test(aa.Go)
    Test(bb.Go)
    Test(cc.Go)
}

Output:

GO: &{FOO}
GO: <nil>
GO: &{}

On the playground: https://play.golang.org/p/V-q2_zwX8h

Solution 2

Another option would be to define the func as a type:

type Process func(a *A)

Then use it as a parameter when calling your other func:

func Test(p Process)

One thing to remember is that these definitions are exactly the same thing:

  • func (a *A) Process() { a.MyVar }
  • func Process(a *A) { a.MyVar }

A pointer receiver is just a func with a local variable of a pointer to the struct. And conversely, a value receiver is a func with a local variable to a value copy of the struct.

Why would you not use a method on the struct, like option 1? There are many reasons. It does seem intuitive to group related methods onto the struct itself like option 1 (especially if coming from other OOP languages like Java or .NET where you normally stick upteen-thousand methods on a single struct). But, since you stated that the struct itself is quite large, this smells of SoC (that it is too large) and may need to be broken up.

Personally, the rule I follow when using option 2 above is:

  • If the func is not using the entire struct's properties (e.g. it is only operating on a sub-set of data, or even none at all), I instead use option 2 with a pointer. (or, use an interface itself with a zero-byte struct)

This allows for much easier unit testing by breaking up my struct that is "quite large" as you say, allowing me to mock up only the interface of functionality I need to support that method.

Now, func definitions are, by definitions, types themselves. So far we have this type:

func(a *A)

This can be used as an input into another method, as you asked for, like so:

func AnotherFunc(fn func(a *A)) {
  a := &A{}
  fn(a)
}

But to me, this makes things a bit hard to read not to mention brittle - someone could change the func definition there and break other things elsewhere.

This is where I prefer to define a type:

type Process func(a *A)

That way, I can consume it like:

func AnotherFunc(p Process) {
  a := &A{}
  p(a)
}

This allows you to access p as your pointer to the func, to pass around as you like. (Note though, you don't have to access the actual pointer of p. IOW, don't do this &p because func types are passed by reference in Golang anyways, just like slices and maps.)

Overall, you typically follow this kind of pattern when you want to break up your logic into smaller manageable (and more testable) pieces - by using smaller, more manageable AnotherFunc() methods to export and unit test an API contract for, while hiding the internals.

Working Example

http://play.golang.org/p/RAJ2t0nWEc

package main

import "fmt"

type Process func(a *A)

type A struct {
  MyVar string
}

func processA(a *A) {
  fmt.Println(a.MyVar)
}

func AnotherFunc(a *A, p Process) {
  p(a)
}

func main() {

    a := &A{
        MyVar: "I'm here!",
    }

    AnotherFunc(a, processA)
}

Unit Testing

Taking the concept of func types to another level, would be to ease unit testing.

You can define global variables for your Process() function:

var Process = func(a *A)

It would continue to be used the exact same way:

 func Test(p Process)

The difference now is during unit testing, you can override the function:

package mypackage_test

import "testing"

func TestProcessHasError(t *testing.T) {
    // keep a backup copy of original definition
    originalFunctionality := Process

    // now, override it
    Process = func(a *A) error {
        // do something different here, like return error
        return errors.New("force it to error")
    }

    // call your Test func normally, using it
    err := Test(Process)

    // check for error
    assert.Error(err)

    // be a good tester and restore Process back to normal
    Process = originalFunctionality 
}

When I get my hands on an existing codebase, these are some of the tricks I start implementing to help decouple the application from itself - and allow for more testing.

Solution 3

You can achieve this with an interface:

type Processor interface {
    Process()
}

func anotherFunction(p Processor) {
    p.Process()
}

...
var a A
anotherFunction(a)
Share:
14,149
William Poussier
Author by

William Poussier

EPITECH 2015 Student

Updated on June 17, 2022

Comments

  • William Poussier
    William Poussier almost 2 years

    Given the following types :

    type A struct {
        ...
    }
    
    func (a *A) Process() {
        ...
    }
    

    I would like to pass the method process of the type A to another function and be able to access the content of the underlying instance of A.

    How should i pass the method to another function? Via a pointer? And how should it be called ?

    The Process() method won't modify the instance of A, i am using a pointer on the method receiver because the struct is quite large. The idea behind my question is to avoid declaring the function Process() outside the struct and pass a ton of arguments to it (instead it access to the members of the struct).

  • William Poussier
    William Poussier about 8 years
    While it isn't answering directly the question, i appreciate the effort of you answer and i thank you because it is very informative and helpfull.