← Back to Blog

Regular Expressions in Go: The RE2 Engine Explained

March 31, 2026 3 min read By CodeTidy Team

The RE2 Engine: Unleashing the Power of Regular Expressions in Go

Have you ever struggled with regular expressions in Go, wondering why your seemingly correct patterns weren't matching as expected? You're not alone. The RE2 engine, Go's built-in regular expression engine, has some unique characteristics that can catch even experienced developers off guard.

Table of Contents

  • Understanding the RE2 Engine
  • Compile vs MustCompile: What's the Difference?
  • Working with Named Groups
  • Finding All Matches with FindAllString
  • Best Practices for Using RE2
  • Key Takeaways
  • FAQ

Understanding the RE2 Engine

The RE2 engine is a non-backtracking regular expression engine, which means it doesn't use backtracking to try different paths in the regular expression. This approach provides several benefits, including improved performance and reduced risk of denial-of-service (DoS) attacks. However, it also requires a different mindset when writing regular expressions. We need to be more explicit and precise in our patterns to avoid unexpected behavior.

For example, consider the following regular expression, which matches a string that starts with "hello" followed by any characters:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile("hello.*")
	fmt.Println(re.FindString("hello world")) // prints "hello world"
}

In this example, the .* matches any characters (including none) after "hello". However, if we want to match a string that starts with "hello" followed by exactly one space, we need to use a more specific pattern:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile("hello ")
	fmt.Println(re.FindString("hello world")) // prints "hello "
}

As you can see, the RE2 engine requires us to be more precise in our patterns to avoid unexpected behavior.

Compile vs MustCompile: What's the Difference?

When working with regular expressions in Go, we often use the Compile function to compile our patterns. However, there's another function called MustCompile that's often overlooked. So, what's the difference?

Compile returns a *Regexp object and an error, which allows us to handle errors explicitly. On the other hand, MustCompile panics if the compilation fails, making it suitable for situations where we're certain the pattern is correct.

We recommend using Compile when working with dynamic patterns or user-input data, as it allows us to handle errors more robustly. However, if we're working with static patterns that are unlikely to change, MustCompile can simplify our code.

Here's an example that demonstrates the difference:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	// Using Compile
	re, err := regexp.Compile("invalid-pattern")
	if err != nil {
		fmt.Println(err) // prints error message
	}

	// Using MustCompile
	re = regexp.MustCompile("invalid-pattern") // panics
}

Working with Named Groups

Named groups are a powerful feature in regular expressions that allow us to capture and refer to specific parts of a match. In Go, we can use named groups by prefixing the group with a name followed by a colon.

For example, consider the following regular expression, which matches a string that contains a username and a password:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile(`(?P<username>\w+) (?P<password>\w+)`)
	match := re.FindStringSubmatch("john smith")
	fmt.Println(match[1]) // prints "john"
	fmt.Println(match[2]) // prints "smith"
}

In this example, we define two named groups: username and password. We can then access the captured values using the FindStringSubmatch function.

Finding All Matches with FindAllString

When working with regular expressions, we often need to find all matches in a string. In Go, we can use the FindAllString function to achieve this.

Here's an example that demonstrates how to use FindAllString:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile("hello")
	matches := re.FindAllString("hello world hello again", -1)
	fmt.Println(matches) // prints ["hello" "hello"]
}

In this example, we use the FindAllString function to find all occurrences of "hello" in the input string. The second argument -1 indicates that we want to find all matches, not just the first one.

Best Practices for Using RE2

When working with the RE2 engine, here are some best practices to keep in mind:

  • Be explicit and precise in your patterns to avoid unexpected behavior.
  • Use named groups to capture and refer to specific parts of a match.
  • Use Compile instead of MustCompile when working with dynamic patterns or user-input data.
  • Use FindAllString to find all matches in a string.

Key Takeaways

  • The RE2 engine is a non-backtracking regular expression engine that provides improved performance and reduced risk of DoS attacks.
  • Be explicit and precise in your patterns to avoid unexpected behavior.
  • Use named groups to capture and refer to specific parts of a match.

FAQ

Q: What's the difference between Compile and MustCompile?

A: Compile returns a *Regexp object and an error, while MustCompile panics if the compilation fails.

Q: How do I access captured values in a named group?

A: You can access captured values using the FindStringSubmatch function.

Q: What's the purpose of the FindAllString function?

A: The FindAllString function finds all matches in a string, not just the first one.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp