Migrating to redis-rs 2 RC? Handle Missing HMGET Fields With Option

A single missing field used to fail your whole typed HMGET call, even with a valid reply from Redis. What does redis-rs 2 RC hand back now, and what else breaks in the migration?

Migrating to redis-rs 2 RC? Handle Missing HMGET Fields With Option
TLDR
  • redis-rs 2 RC changes selected typed command results to vectors of optional entries.
  • An absent field belongs to one position in the reply; it does not mean the whole list is absent.
  • Numeric conversion changes also matter: receiving a negative TTL as u64 can now produce an error.

A valid HMGET reply did not fit the declared Rust type

The typed hmget method in redis-rs 2 RC returns Vec<Option<String>>. A missing field becomes a None entry instead of making conversion to the previous Vec<String> fail.

The Redis command specification already allows this result. HMGET returns values in the requested field order, putting nil in the place of each missing field. Requesting fields from a nonexistent hash returns a list of nil values.

Issue 2311 reported that mismatch in redis 1.5.0: one missing field could fail the entire typed call. The developer’s release-candidate announcement identifies optional return types as a focus of the API cleanup.

Handle absence inside the list

These changes apply to selected methods on TypedCommands and AsyncTypedCommands. They should not be confused with the general Commands API, where callers choose result types. The migration document lists these successful-result shapes:

MethodPreviousVersion 2 RC
hmgetVec<String>Vec<Option<String>>
hget_exVec<String>Vec<Option<String>>
zscore_multipleOption<Vec<f64>>Vec<Option<f64>>
geo_hashVec<String>Vec<Option<String>>

Consider an illustrative profile lookup requesting name followed by city, where only the name exists. The new structure can preserve the name in the first position and represent the absent city in the second.

Callers must decide what each missing entry means. Replacing every None with an empty string loses the distinction between an absent field and a stored empty value. For zscore_multiple, code that previously matched on one outer option must instead consider individual scores.

A successful build does not cover numeric conversion behavior

The candidate also rejects numeric replies that the requested integer type cannot represent. Some previous conversions silently changed the number. Code can therefore compile successfully while handling a server reply differently at runtime.

TTL is a concrete case. It returns remaining seconds, -1 for a key with no expiration, or -2 when the key does not exist. An unsigned u64 cannot preserve those negative states. The migration guide shows receiving the result as a signed integer:

let ttl: i64 = con.ttl("key")?;

Handle the two negative sentinels before treating the remaining value as seconds. An error converting a negative reply to an unsigned type does not mean the server returned an invalid TTL.

Check missing entries and sentinel values explicitly

Useful migration cases follow directly from the response shapes: all requested hash fields present, one field absent, and the entire hash absent. For TTL, check an expiring key, a persistent key and a nonexistent key.

Version 2 is still a release candidate, and its breaking-change document also covers async futures, TLS configuration and other APIs. These return-type fixes are one part of an upgrade.

The immediate task is to preserve the server’s distinction between a value and its absence, then make the application’s response to each state explicit.