6

I was wondering if doing something like this in Go is even possible.

type MyStruct struct {
    id int
}

func (ms *MyStruct) PrintHello() {
    fmt.Printf("Hello from original method %v", ms.id)
}

func main() {
    fmt.Println("Hello, playground")
    m := MyStruct{}
    m.PrintHello()

    m.PrintHello = func() {fmt.Printf("Hello from newer method 2")}
}

Error: cannot assign to m.PrintHello

https://play.golang.org/p/2oJQFFH4O5

Sorry if this doesn't make sense for Go programmers, I am new to Go and wondering if some of the things that can be done in dynamically typed languages can be done in Go.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
ultimoo
  • 495
  • 2
  • 5
  • 13
  • 1
    This answer might provide some insight http://stackoverflow.com/questions/21251242/is-it-possible-to-call-overridden-method-from-parent-struct-in-golang – Adam Mar 31 '17 at 19:09
  • 3
    https://play.golang.org/p/Lp3aP2dqpt – mkopriva Mar 31 '17 at 19:22
  • @mkopriva 's particular way of doing this is probably more preferred than mine. But then again, it still contains the issue of being able to access the internal struct data members. But at least the Method itself is a real method. – RayfenWindspear Mar 31 '17 at 19:54

1 Answers1

15

Given that Go is a statically typed language, you cannot do this specific piece of code. However, functions are variables, so you CAN do something like this. Just remember that this is technically NOT the same as assigning a new method, as JimB states in the comments.

https://play.golang.org/p/rfuCzXD8fP

package main

import (
    "fmt"
)

type MyStruct struct {
    id         int
    PrintHello func(ms * MyStruct)
}

func (ms *MyStruct) init() {
    ms.PrintHello = func(ms *MyStruct) { fmt.Printf("Hello from original method %v\n", ms.id) }
}

func main() {
    m := &MyStruct{id: 42}
    m.init()
    m.PrintHello(m)

    m.PrintHello = func(ms *MyStruct) { fmt.Printf("Hello from newer method 2 %d\n", ms.id) }
    m.PrintHello(m)
}

Because this function is not really a method, you can only access the internal struct values by passing it as an argument. This means that if this ever has to cross package boundaries, unexported values will not be available.

It is also important to note that the function type needs to be the same. So if you defined the function as PrintHello func(ms * MyStruct) error in the struct, you would need to assign a function that returns an error.

RayfenWindspear
  • 6,116
  • 1
  • 30
  • 42
  • This is not a method though, therefor it is not part of the method set. – JimB Mar 31 '17 at 19:29
  • 1
    @JimB True, but this is an alternative way to do what is desired. If modifying methods is really what is wanted, I don't think it can be done. – RayfenWindspear Mar 31 '17 at 19:32
  • Actually, no, you are right, this is not ideal because you cannot access the struct values this way. You could define the function var to accept the struct pointer though `PrintHello func(ms *MyStruct)`. Answer edited to account for this. – RayfenWindspear Mar 31 '17 at 19:40
  • 1
    You can definitely use plug-and-play methods using the same strategy: https://play.golang.org/p/s7xmU33dXY. Downside is the ad hoc methods cannot access the fields of the structure in question unless the structure is passed as an argument to the functions, which is annoying but not a game-breaker: https://play.golang.org/p/luLt8bVq80 – Kaedys Mar 31 '17 at 20:18
  • Looking forward, there also another problem with doing this beyond package boundaries if the struct fields are not exported. Testing this I got `ms.id undefined (cannot refer to unexported field or method id)` – RayfenWindspear Mar 31 '17 at 21:22
  • this solution also won't allow to wrap MyStruct's functionality inside an interface, which limits the usability of this approach – Omn Feb 28 '18 at 00:56