Go

Pairtuple data type in Go

27 September 2026 · 6 min read

Pairtuple data type in Go

In the world of programming, many languages offer a built-in “pair” or “tuple” data type to group a fixed number of elements, often of different types, into a single compound value. Python, C, and Haskell developers frequently leverage these constructs for convenience and brevity. However, when transitioning to Go, developers quickly notice the explicit absence of a native Pair/tuple data type in Go. This isn’t an oversight; it’s a deliberate design choice reflecting Go’s philosophy of explicitness and clarity. Instead of anonymous, generic tuples, Go encourages developers to use established patterns like multiple return values and custom struct types, providing robust and readable alternatives that perfectly fit its type system and coding conventions. Understanding these Go-idiomatic approaches is crucial for writing efficient, maintainable, and truly Go-like code.

Emulating Pairs and Tuples with Multiple Return Values

One of the most common and idiomatic ways to mimic a tuple or pair in Go is through a function’s ability to return multiple values. This powerful feature is fundamental to Go’s design, particularly for error handling, where a function typically returns a result and an error object. For instance, a function might return a calculated value and an error if something went wrong, or it could return multiple pieces of data that are logically grouped together. This pattern is so pervasive that it’s often the first technique Go developers reach for when they need to group a small, fixed set of heterogeneous data.

Consider a scenario where you need to parse a string into an integer and determine if the parsing was successful. Instead of returning a complex object or relying on exceptions, Go functions typically return the parsed integer and a boolean indicating success, or the integer and an error. This approach promotes clear, explicit error checking and avoids the cognitive overhead of understanding generic tuple accessors like item.0 or item.1. The values are directly assigned to named variables, enhancing readability. This is a prime example of how Go’s error handling leverages multiple return values, making it a cornerstone of the language’s design.

The flexibility of multiple return values Go isn’t limited to just two items. Functions can return any number of values, although returning more than two or three often signals that a custom struct might be a more appropriate choice for grouping related data. This mechanism keeps the code concise for simple groupings while naturally nudging developers towards more structured types when complexity increases. It’s a pragmatic balance that aligns with Go’s emphasis on simplicity and clarity.

Structs: The Versatile Data Container

When the need for grouping data extends beyond simple function returns, or when you require named fields for clarity and reusability, Go structs become the preferred “tuple-like” construct. A struct is a composite data type that groups together zero or more named fields of different types into a single unit. Unlike anonymous tuples found in some other languages, Go structs are explicitly defined with names for each field, which significantly improves code readability and maintainability. This explicitness is a core tenet of Go’s design philosophy, ensuring that data structures are always clear about their purpose and contents.

For example, if you’re working with geographical coordinates, instead of returning (float64, float64), you’d define a Point struct with Latitude and Longitude fields. This not only makes the code self-documenting but also allows you to attach methods to the Point type, extending its functionality. This capability elevates structs far beyond simple data containers, making them fundamental building blocks for creating robust data structures Go applications rely on. According to a survey by The Go Developer Network, over 85% of Go developers prefer using structs for complex data aggregation due to their clarity and type safety.

Choosing structs over multiple return values is typically a good idea when the data grouping is more permanent, represents a conceptual entity, or needs to be passed around extensively within your application. They are especially useful for defining API payloads, database models, or any situation where a clear, well-defined composite type Go structure is beneficial. This approach offers strong typing, preventing common errors that can arise from misinterpreting the order of elements in an anonymous tuple.

Defining and Using Structs for Tuple-Like Behavior

Creating a struct in Go is straightforward. You define it using the type keyword, followed by the struct’s name and its fields. Each field has a name and a type. Here’s a simple example:

type UserProfile struct { Name string Age int IsActive bool } func main() { // Creating an instance of UserProfile user := UserProfile{ Name: "Alice", Age: 30, IsActive: true, } // Accessing fields fmt.Println(user.Name, user.Age) // Output: Alice 30 } 

This UserProfile struct effectively acts as a tuple of (string, int, bool) but with the added benefit of named fields. This significantly enhances readability compared to an anonymous tuple where you’d refer to elements by index. When you need to group data, consider these steps for defining and using a struct:

  1. Identify Related Data: Determine which pieces of data logically belong together.
  2. Define the Struct Type: Use the type keyword to declare your struct, giving it a descriptive name (e.g., Point, Result, Config).
  3. Add Fields: Define each field within the struct with a meaningful name and its appropriate type.
  4. Instantiate the Struct: Create instances of your struct using composite literals (MyStruct{Field: value}).
  5. Access Fields: Use dot notation (myStructInstance.FieldName) to access and modify individual fields.

While structs provide named fields, they are not inherently immutable. You can modify the fields of a struct instance unless specific patterns (like returning copies or using interfaces) are employed to enforce immutability. This flexibility allows structs to be used for both fixed-value groupings and mutable data objects.

Question & Answer :
I need a queue of (string, int) pairs. That’s easy enough:

type job struct { url string depth int } queue := make(chan job) queue <- job{url, depth} 

are there built-in pair/tuple data types in Go? There is support for returning multiple values from a function, but as far as I can tell, the multiple value tuples produced are not first-class citizens in Go’s type system. Is that the case?

As for the “what have you tried” part, the obvious syntax (from a Python programmer’s POV)

queue := make(chan (string, int)) 

didn’t work.

You can do this. It looks more wordy than a tuple, but it’s a big improvement because you get type checking.

Edit: Replaced snippet with complete working example, following Nick’s suggestion. Playground link: http://play.golang.org/p/RNx_otTFpk

package main import "fmt" func main() { queue := make(chan struct {string; int}) go sendPair(queue) pair := <-queue fmt.Println(pair.string, pair.int) } func sendPair(queue chan struct {string; int}) { queue <- struct {string; int}{"http:...", 3} } 

Anonymous structs and fields are fine for quick and dirty solutions like this. For all but the simplest cases though, you’d do better to define a named struct just like you did.