I’m implementing a simple task queue using redis in Rust, but am struggling to deserialize the returned values from redis into my custom types.
In total I thought of 3 approches:
- Deserializing using serde-redis
- Manually implementing the
FromRedisValue
trait - Serializing to String using serde-json > sending as string > then deserializing from string
The 3rd approach worked but feels artificial. I’d like to figure out either 1 or 2, both of which I’m failing at.
Approach 1 – serde-redis
I have a simple Task definition:
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Task {
pub id: u32,
pub desc: String,
}
which I’m trying to receive into a worker:
use serde_redis::RedisDeserialize;
pub fn execute_task(conn: &mut Connection) {
let task: Task = redis::cmd("LPOP")
.arg("task_q")
.query::<Task>(conn)
.unwrap()
.deserialize()
.unwrap();
println!("... executing task {} ...", task.id);
}
but I’m getting the following error:
error[E0277]: the trait bound `Task: FromRedisValue` is not satisfied
--> src/bin/worker.rs:38:10
|
38 | .query::<Task>(conn)
| ^^^^^ the trait `FromRedisValue` is not implemented for `Task`
error[E0599]: no method named `deserialize` found for struct `Task` in the current scope
--> src/bin/worker.rs:40:10
|
40 | .deserialize()
| ^^^^^^^^^^^ method not found in `Task`
So clearly I integrated the crate wrong, as it’s not working. The documentation is super brief and the source code way over my head as a beginner – what could I be missing?
Approach 2 – manually implementing FromRedisValue
My naive approach:
impl FromRedisValue for Task {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
let t = Task {
id: v.id,
desc: v.desc,
};
RedisResult::Ok(t)
}
fn from_redis_values(items: &[Value]) -> RedisResult<Vec<Self>> {
let tasks = items
.into_iter()
.map(|v| Task {
id: v.id,
desc: v.desc,
})
.collect();
RedisResult::Ok(tasks)
}
}
The errors I’m getting:
error[E0609]: no field `id` on type `&Value`
--> src/redis_tut.rs:203:19
|
203 | id: v.id,
| ^^
error[E0609]: no field `desc` on type `&Value`
--> src/redis_tut.rs:204:21
|
204 | desc: v.desc,
| ^^^^
// ...the same for the vector implementation
So clearly redis’s Value doesn’t have / know of the fields I want for Task. What’s the right way to do this?
2
Answers
Redis doesn’t define structured serialization formats. It mostly store strings and integers. So you have to choose or define your format for your struct.
A popular one is JSON, as you noticed, but if you just want to (de)serialize simple pairs of (id, description), it’s not very readable nor convenient.
In such a case, you can define your own format, for example the id and the description with a dash in between:
I actually wrote a crate for this kind of Problem, it provides Derive Macros that implements the redis::FromRedisValue and redis::ToRedisArgs traits from mitsuhiko / redis-rs for any struct in which every field’s type also implements ToRedisArgs. Check it out redis-redive.