gogenerics

Converting `func() Foo` to `func() any` in Golang?


Issue

The immediate issue is that a function of type func() Foo cannot be assigned func() any in Golang. Perhaps this is an XY problem, so I will explain why exactly I am even in this situation.

Example

I essentially have something like a health check package as such (this is a simplified model for sake of simplicity):

//package: internal/health
type Check struct {
    Name  string
    Func func() (metadata any, err error)
}

type Manager struct {
    checks []Check
}

func (m *Manager) RegisterCheck(check Check) {
    m.checks = append(m.checks , check)
}

func (m *Manager) PrintHealth() {
    for _, check := m.checks {
        output, _ := check.Func()
        slog.Info("Health", slog.Any("output", output))
        // In the actual implementation, I aggregate the checks and JSONify them
    }
}

Then perhaps I have another package that has a methods which I want to add to checks:

//package: internal/service
type Foo struct {}

func (f Foo) Health() (string, error) {
    return "baz", nil
}

type Bar struct {}

func (b Bar) Health() (map[string]int, error) {
    return map[string]int{"qux": 1} , nil
}

//package: cmd/app
func main() {
    foo := service.Foo{}
    bar := service.Bar{}

    h := health.Manager{}
    h.RegisterCheck(health.Check{
        Name: "Foo",
        Func: foo.Health, // This is where the type error occurs
    })
    h.RegisterCheck(health.Check{
        Name: "Bar",
        Func: bar.Health, // This is where the type error occurs
    })
}

What have I tried/considered?


Solution

  • The simplest solution is to define the Health functions to conform the interface signature:

    func (f Foo) Health() (any, error) {
        return "baz", nil
    }
    
    func (b Bar) Health() (any, error) {
        return map[string]int{"qux": 1} , nil
    }
    

    If the purpose is simply to log the return value, this should work.

    If you want to keep the return types, then the next option is to use adapters:

      h.RegisterCheck(health.Check{
            Name: "Foo",
            Func: func() (any,error) {
                return foo.Health()
            },
        })
        h.RegisterCheck(health.Check{
            Name: "Bar",
            Func: func() (any,error) {
                return bar.Health()
            },
        })