Better way to get field by type

I have a struct like this:

struct Foo {
    v1: Vec<i32>,
    v2: Vec<char>,
    v3: Vec<String>,
}

and I want to implement a function, which take a vector by a given type:

impl Foo {
    fn getSlice<T>(&self) -> &[T] {/* any way to do this? */ }
}

Currently my solution is to design a trait like this:

impl PickVec for i32 {
    type Ty = i32;

    fn pick(foo: &Foo) -> &[Self::Ty] {
        foo.v1.as_slice()
    }
}

impl Foo {
    fn getSlice<T: PickVec>(&self) -> &[T] {
        T::pick(self)
    }
}

Even though it works fine, but it looks way too weird to me, so is there any better way?

In my case, the element type of vectors are unique, there won't be two fields

v1: Vec<u32>,
v2: Vec<u32>,

P.S. I tried to use std::Any, but it seems that std::Any only works on value, not types.

I think the more important question is why you want to do this?

I really don't know how to answer "why", it's complicated and has nothing to do with the question.

The real case is that type T is a serialization type, the getSlice (not the real name) will first extract bytes, then deserialize it, then put the decoded object to a proper vector.

Again, I just designed it like this, and getSlice is a function to choose a proper vector by a given type, that's "why" I can expain for now.

Given that, I'd say it is a good solution. AFAIK, trait items are the only way in Rust to "choose" a type in a generic context, so long as you're not using macros.
With macros, the situation changes a bit. With standard macros, you might be able to do it. With proc macros, you'd definitely be able to do this. Whether you want to take that much effort depends on how many types you're dealing with.

How can a derive macro do this? I can use derive macro to generate code like

impl Foo {
    fn get_i32_slice(&self) -> &[i32] {/* ... */ }
    fn get_string_slice(&self)-> &[String] {}
}

But in this way it loses the generic, in my case there's no way to check what T is and call the proper function manually.

If you need to run different code for different types under the same function name based on a generic type, you'll need a trait. Point finale.

1 Like

There's no need to define your own trait; you can use AsRef<T> for this:

struct Foo {
    v1: Vec<i32>,
    v2: Vec<char>,
    v3: Vec<String>,
}

impl AsRef<[i32]> for Foo {
    fn as_ref(&self)->&[i32] { self.v1.as_ref() }
}

impl AsRef<[char]> for Foo {
    fn as_ref(&self)->&[char] { self.v2.as_ref() }
}

impl AsRef<[String]> for Foo {
    fn as_ref(&self)->&[String] { self.v3.as_ref() }
}

impl Foo {
    pub fn get_slice<T>(&self)->&[T] where Self: AsRef<[T]> {
        self.as_ref()
    }
}
2 Likes

Your solution is more concise, I really don't want to implement my own trait in this case.

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.