How impl a trait of FromRedisValue for more structs

Just look at the simple code:

use redis::{FromRedisValue, Value, RedisResult, Commands, from_redis_value, ErrorKind};
use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct Uids(Vec<u16>);

#[derive(Deserialize, Debug)]
pub struct Ages(Vec<u8>);

impl FromRedisValue for Uids {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

impl FromRedisValue for Ages {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

pub fn get_value<T>() -> RedisResult<Option<T>> where
  T: FromRedisValue {
  let client = redis::Client::open("redis://127.0.0.1:6379/").unwrap();
  let mut con = client.get_connection().unwrap();

  con.get("json")
}

fn main() {

  match get_value::<Uids>() {
    Ok(redis_value) => match redis_value {
      Some(result) => println!("{:?}", result),
      None => println!("No value in Redis"),
    },
    Err(err) => {
      println!("Error: {}", err);
    }
  }

  match get_value::<Ages>() {
    Ok(redis_value) => match redis_value {
      Some(result) => println!("{:?}", result),
      None => println!("No value in Redis"),
    },
    Err(err) => {
      println!("Error: {}", err);
    }
  }
}

The value of key "json" is "[65, 28]" in Redis, Running then will print

   Compiling rust-trait-generics v0.1.0 (D:\code\rust-trait-generics)
    Finished dev [unoptimized + debuginfo] target(s) in 1.17s
     Running `target\debug\rust-trait-generics.exe`
Uids([65, 28])
Ages([65, 28])

And now, you can see that the struct Uids and Ages all the structs need to impl a FromRedisValue trait, but there is a duplicate code there

impl FromRedisValue for Uids {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

impl FromRedisValue for Ages {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

So I felt something I was wrong on it. How can I to doing like that

impl FromRedisValue for Uids, Ages {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

Help me to do that, please, thank you!

Unfortunately, you cannot do it exactly the way you have specified it.
However, you can make a function of the form fn from_redis_value<T>(v: &Value) -> RedisResult<T> where ... {} and call that in the trait impl.
Now what trait bounds you would put on T depends on what functions you are using in the implementation. I think Serialize would suffice here, although I may be wrong. If a standard trait doesn't fit, then you can make your own trait and put that as the trait bound. But that also means that you'd have to implement that trait for Uids and Ages - so it's not so attractive a solution anymore.

I'm still confused for it. sorry, I'm a new guy in Rust, I try it as you say just like this:

impl<T> FromRedisValue for T where T: Deserialize<'static> {
  fn from_redis_value(v: &Value) -> RedisResult<Self> {
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<Self>(&json_str) {
      Ok(v) => v,
      Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into())
    };

    Ok(result)
  }
}

But there are some errors printed when running

# cargo run
error[E0119]: conflicting implementations of trait `redis::FromRedisValue` for type `bool`
  --> src\main.rs:38:1
   |
38 | impl<T> FromRedisValue for T where T: Deserialize<'static> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `redis`:
           - impl FromRedisValue for bool;

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> src\main.rs:38:6
   |
38 | impl<T> FromRedisValue for T where T: Deserialize<'static> {
   |      ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
   = note: only traits defined in the current crate can be implemented for a type parameter

Some errors have detailed explanations: E0119, E0210.
For more information about an error, try `rustc --explain E0119`.
warning: `rust-trait-generics` (bin "rust-trait-generics") generated 1 warning
error: could not compile `rust-trait-generics` due to 2 previous errors; 1 warning emitted

Am I missing something or where am I incorrent at it?

Actually what I meant is something like:

fn convert<T>(v: &Value) -> RedisValue<T>
where
    T: Deserialize<'static>,
{
    let json_str: String = from_redis_value(v)?;

    let result: Self = match serde_json::from_str::<T>(&json_str) {
        Ok(v) => v,
        Err(_err) => return Err((ErrorKind::TypeError, "Parse to JSON Failed").into()),
    };

    Ok(result)
}

impl FromRedisValue for Udis {
    fn from_redis_value(v: &Value) -> RedisValue<Self> {
        convert(v)
    }
}

impl FromRedisValue for Ages {
    fn from_redis_value(v: &Value) -> RedisValue<Self> {
        convert(v)
    }
}

For the function convert, I used Deserialize as the trait bound for T. I'm not fully sure that it is the correct trait bound. But other than that, the structure of the above code should work.

I got it~, it seems a good idea, but is there a way to do just one impl for all structs? I thinking about it around the concept like Generics? Trait extend?

There is a better way I can't find out in my case.

Not really. I believe there is an RFC for this but I can't quite remember the name or the number.

Oh~~, please ~~ take a thinking for it, don't leave it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.