Features Tutorials

Refactorings in GoLand: Extract and Inline

In this blog post, we will cover the Extract and Inline refactoring options as we continue to explore the refactorings provided by the IDE.

If you want to check out the other articles from this series, you can find them all here (links will be updated as new articles are published):

Download GoLand 2018.3 EAP

With these two refactorings, you can quickly clean up your code and make it more streamlined or explicit. But that is enough of an introduction, actions speak louder than words, so let’s have a look at them in action.

Take the following code for example:

package main

import (
	"errors"
	"fmt"
	"log"
)

type User struct {
	ID   int
	Name string
}

func findUser(user string) (*User, error) {
	if user != "florin" {
		return nil, errors.New("user not found")
	}
	return &User{
		ID:   1,
		Name: "Florin",
	}, nil
}

func main() {
	user, err := findUser("florin")
	if err != nil {
		log.Fatalf("could not find user %s\n", "florin")
	}

	fmt.Printf("user found: %#v\n", user)
}

Here we can apply the Extract refactoring to a number of places.

We can select the ` &User{ID: 1, Name: “Florin”,} ` portion and use the Extract Variable refactoring (Ctrl+Alt+V on Windows/Linux and Cmd+Alt+V on macOS) which will result in a code similar to this:

func findUser(user string) (*User, error) {
	if user != "florin" {
		return nil, errors.New("user not found")
	}
	u := &User{
		ID:   1,
		Name: "Florin",
	}
	return u, nil
}

Refactorings - Extract Variable - new

Extract Variable refactoring can be applied to different kinds of expressions, including conditions such as ` user != “florin” `, and we can get the code looking like this:

func findUser(user string) (*User, error) {
	notMatches := user != "florin"
	if notMatches {
		return nil, errors.New("user not found")
	}
	u := &User{
		ID:   1,
		Name: "Florin",
	}
	return u, nil
}

Refactorings - Extract Variable 2

Another feature of extract refactoring is that we can use it to extract the code in a method or function. We can create a constructor function for the User variable.
Select the ` &User{ID: 1, Name: “Florin”,} ` and choose Extract Method (Ctrl+Alt+M on Windows/Linux and Cmd+Alt+M on macOS) to produce code similar to:

func findUser(user string) (*User, error) {
	notMatches := user != "florin"
	if notMatches {
		return nil, errors.New("user not found")
	}
	u := newUser()
	return u, nil
}

func newUser() *User {
	return &User{
		ID:   1,
		Name: "Florin",
	}
}

Refactorings - Extract Method

We can also choose to inline the ` notMatches ` variable to make the code more readable. To do so, place the cursor over the ` notMatches ` and invoke the Inline refactoring (Ctrl+Alt+N on Windows/Linux and Cmd+Alt+N on macOS).
The resulting code will be similar to this:

func findUser(user string) (*User, error) {
	if user != "florin" {
		return nil, errors.New("user not found")
	}
	u := newUser()
	return u, nil
}

Refactorings - Inline Variable

You can think of the Inline refactoring as the opposite of the Extract refactoring.

There are some limitations in the current implementations of these refactorings.
For example, the Extract refactoring cannot extract code which contains a ` return ` statement. And while it’s possible to extract code which contains the ` defer ` statement, you’ll receive a warning about this, as it’s likely to cause changes to the original intent of the code.
The Inline refactoring also cannot currently inline functions.

Well, that’s it for this article. We’ve seen how to use two powerful options from the IDE, in order to make our code terser.

As we continue this series on refactorings, we look forward to your feedback both on what we can talk about in future posts, as well as what features you’d like to see in the IDE.
General feedback about the IDE is very welcome, so please don’t hesitate to share your thoughts either in the comments section below, on our issue tracker, or on Twitter.

image description