{
  "id": "scan-buffer",
  "title": "Handle command results efficiently",
  "url": "https://redis.io/docs/latest/develop/clients/go/scan-buffer/",
  "summary": "Scan go-redis command results into Go values and use buffers for large string payloads.",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-07-02T11:31:07-05:00",
  "page_type": "content",
  "content_hash": "f1050ac81311bf61d8e2f3a7dbebe123c49747148b02f4f8569458e78a251ac4",
  "sections": [
    {
      "id": "initialize",
      "title": "Initialize",
      "role": "content",
      "text": "Import the packages you need:\n\nFoundational: Import go-redis and supporting packages for scanning and buffer examples\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nConnect to Redis:\n\nFoundational: Connect to Redis before running scan and buffer examples\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"
    },
    {
      "id": "scan-hashes-into-structs",
      "title": "Scan hashes into structs",
      "role": "content",
      "text": "Use struct tags of the form `redis:\"field\"` to map Redis hash fields to Go\nstruct fields. The `Scan()` method converts matching fields to the destination\ntypes and returns an error if a conversion fails.\n\nThe following example stores a hash with [`HSET`](https://redis.io/docs/latest/commands/hset)\nand then scans the result of [`HGETALL`](https://redis.io/docs/latest/commands/hgetall)\ninto a `Bike` struct:\n\nStructured results: Scan all hash fields into a Go struct using redis field tags\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also scan a subset of fields with [`HMGET`](https://redis.io/docs/latest/commands/hmget).\nFields that are not returned keep their Go zero values:\n\nStructured results: Scan selected hash fields and leave missing fields at their zero values\n\n**Difficulty:** Intermediate\n\n**Builds upon:** scan_hash\n\n**Available in:** Go\n\n##### Go\n\n[code example]"
    },
    {
      "id": "scan-lists-into-slices",
      "title": "Scan lists into slices",
      "role": "content",
      "text": "Commands that return a list of strings, such as [`LRANGE`](https://redis.io/docs/latest/commands/lrange),\ncan use `ScanSlice()` to convert the result into a typed Go slice:\n\nStructured results: Convert a list command result into a typed Go slice with ScanSlice\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"
    },
    {
      "id": "use-byte-buffers",
      "title": "Use byte buffers",
      "role": "content",
      "text": "For large string payloads, you can avoid creating a new string for every read by\nproviding a caller-owned byte buffer. Use `SetFromBuffer()` to write a `[]byte`\nvalue and `GetToBuffer()` to read the value into an existing buffer.\n\n\nThe buffer APIs require `github.com/redis/go-redis/v9` v9.21.0\nor later.\n\n&nbsp;\n\nBuffer optimization: Write and read Redis string values using caller-owned byte buffers\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`GetToBuffer()` returns a `*redis.ZeroCopyStringCmd`. Use `Val()` or `Result()`\nto get the number of bytes read, `Bytes()` to access the populated slice\n(`buf[:n]`), and `Err()` to check for errors such as `redis.Nil` when the key\ndoes not exist.\n\n\n`GetToBuffer()` requires a buffer large enough to hold the whole value. It also\nopts out of automatic retries because a failed read might already have written\npartial data into your buffer. If a buffer is too small, `Err()` reports a\n`buffer too small` error.\n\n\nThe following example shows how to detect a buffer that is too small:\n\nBuffer sizing: Detect and handle a buffer that is too small for the Redis value\n\n**Difficulty:** Intermediate\n\n**Builds upon:** buffer_round_trip\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`SetFromBuffer()` does not set an expiration. If you need a TTL, call\n[`EXPIRE`](https://redis.io/docs/latest/commands/expire) separately with `Expire()`, or use\n`Set()` with an expiration when the extra allocation is acceptable."
    },
    {
      "id": "more-information",
      "title": "More information",
      "role": "content",
      "text": "See the [`go-redis`](https://github.com/redis/go-redis) repository for more\nexamples and API details."
    }
  ],
  "examples": [
    {
      "id": "initialize-ex0",
      "language": "go",
      "code": "import (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/redis/go-redis/v9\"\n)",
      "section_id": "initialize"
    },
    {
      "id": "initialize-ex1",
      "language": "go",
      "code": "ctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})\n\tdefer rdb.Close()",
      "section_id": "initialize"
    },
    {
      "id": "scan-hashes-into-structs-ex0",
      "language": "go",
      "code": "type Bike struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"brand\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tif err := rdb.HSet(ctx, \"scanbuf:bike:1\",\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"price\", 4972,\n\t).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar bike Bike\n\tif err := rdb.HGetAll(ctx, \"scanbuf:bike:1\").Scan(&bike); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %s, brand: %s, price: $%d\\n\",\n\t\tbike.Model, bike.Brand, bike.Price)\n\t// >>> Model: Deimos, brand: Ergonom, price: $4972",
      "section_id": "scan-hashes-into-structs"
    },
    {
      "id": "scan-hashes-into-structs-ex1",
      "language": "go",
      "code": "type BikeStock struct {\n\t\tModel string `redis:\"model\"`\n\t\tStock int    `redis:\"stock\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar partial BikeStock\n\tif err := rdb.HMGet(ctx, \"scanbuf:bike:1\", \"model\", \"stock\").Scan(&partial); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %s, stock: %d, price: $%d\\n\",\n\t\tpartial.Model, partial.Stock, partial.Price)\n\t// >>> Model: Deimos, stock: 0, price: $0",
      "section_id": "scan-hashes-into-structs"
    },
    {
      "id": "scan-lists-into-slices-ex0",
      "language": "go",
      "code": "if err := rdb.RPush(ctx, \"scanbuf:stock\", 3, 4, 5).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar stockCounts []int\n\tif err := rdb.LRange(ctx, \"scanbuf:stock\", 0, -1).ScanSlice(&stockCounts); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"Stock counts:\", stockCounts)\n\t// >>> Stock counts: [3 4 5]",
      "section_id": "scan-lists-into-slices"
    },
    {
      "id": "use-byte-buffers-ex0",
      "language": "go",
      "code": "payload := []byte(\"compact bike data\")\n\n\tif err := rdb.SetFromBuffer(ctx, \"scanbuf:payload\", payload).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(\"OK\") // >>> OK\n\n\tbuf := make([]byte, len(payload))\n\tcmd := rdb.GetToBuffer(ctx, \"scanbuf:payload\", buf)\n\tn, err := cmd.Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Read %d bytes: %s\\n\", n, string(cmd.Bytes()))\n\t// >>> Read 17 bytes: compact bike data",
      "section_id": "use-byte-buffers"
    },
    {
      "id": "use-byte-buffers-ex1",
      "language": "go",
      "code": "smallBuf := make([]byte, 4)\n\tsmallCmd := rdb.GetToBuffer(ctx, \"scanbuf:payload\", smallBuf)\n\n\tif err := smallCmd.Err(); err != nil {\n\t\tif strings.Contains(err.Error(), \"buffer too small\") {\n\t\t\tfmt.Println(\"Buffer too small\")\n\t\t} else {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\t// >>> Buffer too small",
      "section_id": "use-byte-buffers"
    }
  ]
}
