1

I'm using Visual Studio Code with the official Go extension and the official Go tools. My Go version is go1.19.

When I rename an interface's function, it doesn't affect its implementations which leads to to compiler errors.

Sample Code:

package main

import "fmt"

type animal interface {
    makeSound()
}

type dog struct {
    name string
}

func (d *dog) makeSound() {
    fmt.Printf("%s: bark! bark!", d.name)
    fmt.Println()
}

func main() {
    dog1 := &dog{
        name: "dog1",
    }

    patHead(dog1)
}

func patHead(a animal) {
    a.makeSound()
}

Output:

dog1: bark! bark!

The Problem:

If I try to rename dog.makeSound() (e.g. to dog.makeNoise()), I get the following error:

renaming this method "makeSound" to "makeNoise" would make *project.dog no longer assignable to interface animal (rename project.animal.makeSound if you intend to change both types)

The error makes sense because the programmer might have missed that the struct was implementing an interface, so a recursive rename that affects the interface and other implementations would be unintentional. I don't have a problem with this error.

As the error message instructs, you can rename the interface's function. The rename affects users of that interface:

type animal interface {
    makeNoise()
}

func patHead(a animal) {
    a.makeNoise()
}

However, it leaves its implementations unaffected, which now generates an error:

type dog struct {
    name string
}

func (d *dog) makeSound() {
    fmt.Printf("%s: bark! bark!", d.name)
    fmt.Println()
}

func main() {
    dog1 := &dog{
        name: "dog1",
    }

    patHead(dog1)
}

Error:

cannot use dog1 (variable of type *dog) as animal value in argument to patHead: *dog does not implement animal (missing method makeNoise)

The problem is that now that there is a compiler error, you can't rename the implementation's method to match the interface. Below is the error when you try to rename dog.makeSound() to dog.makeNoise():

renaming "makeSound" to "makeNoise" not possible because "project" has errors

Renaming interface functions should be as simple as renaming a variable or class function, and Go's default tools handle the latter scenarios very well. However, it doesn't handle the problem stated above.

GoLand (which isn't free) supports renaming interface implementations as documented here, but assuming you can only use free tools, how do you handle this scenario?

Find and Replace isn't safe because it could affect unrelated code, and manually fixing all the compiler errors generated after renaming the interface function is tedious if you have many implementations in your project.

UPDATE:

I submitted a gorename issue here.

However, I want to keep this question open in case my bug doesn't get fixed and I can accept a solid workaround in the meantime for this common problem in Go.

Floating Sunfish
  • 4,920
  • 5
  • 29
  • 48
  • This question isn't related to Go, but to third party tooling Nevertheless, if you have an interface with a set of methods, any data type with that set of methods will implement that interface. So, you can first find all the implementations of that interface, change the interface, and then refactor the code manually. If you want to make sure such implementations are sane, use `var _ someInterface = someType{}` when you declare a type implementing an interface, and you'll get compile errors when you change the interface. – Burak Serdar Aug 03 '22 at 03:25
  • Thank you for the suggestion on how to check how many errors will need to be manually fixed after renaming an interface function. However, I believe the question is still related to Go because Go uses its built-in renaming tool (`gorename`) for a very common refactoring task. Unless I'm mistaken and this task is indeed done by a third-party tool, in which case I will close this question and submit a new Issue on its page. – Floating Sunfish Aug 03 '22 at 03:34
  • 1
    I take that comment back. I didn't know about `gorename`. Thanks for pointing that out. – Burak Serdar Aug 03 '22 at 03:36
  • 1
    Implementing interfaces in Go is implicit, there is no declaration of intent. A type implementing an interface may or may not be intentional. That being said, it's not guaranteed renaming an interface requires to rename a certain method with identical signature, that method may or may not exist to implement the interface. If a method does not exist to implement an interface, then renaming the interface's method should not result in renaming the concrete type's method too. – icza Aug 03 '22 at 08:16
  • Also, if a type implements 2 interfaces having a method in common, renaming that method in one interface should or should not involve renaming the method implementation. It may or may not be wanted, but a tool cannot make a decision that satisfies all needs (both outcomes may be valid). – icza Aug 03 '22 at 08:16
  • Hmm, this is more complicated than I thought. Perhaps an `implements` keyword really was the solution, because not having to declare what interfaces you implement is a pretty terrible tradeoff for not being able to easily rename said interface functions. Maybe this can be added as a proposal to Go 2 or something. – Floating Sunfish Aug 03 '22 at 09:52
  • Also, perhaps `gorename` can just check where the interface is being used and check what structs are being passed as parameters so the change will only affect structs that are directly involved with it. – Floating Sunfish Aug 03 '22 at 09:56
  • 1
    @FloatingSunfish I wouldn't count on `implements` getting added in Go 2, this is by design: [Why doesn't Go have "implements" declarations?](https://go.dev/doc/faq#implements_interface) – icza Aug 03 '22 at 10:39
  • Thanks for the heads up, @icza. I'll consider this if I ever propose a fully-optional `implements` keyword. At the very least, the Go team should consider a more elegant solution to this common refactoring action because manually renaming broken function calls across interface implementations is definitely not "elegant." – Floating Sunfish Aug 04 '22 at 00:01

0 Answers0