genericsstructrustnewtype

Creating a Generic Struct Indexed by a Generic NewType


I'd like to create a generic struct that uses a generic newtype to index. Ideally it would look something like this:

use std::marker::PhantomData;

struct Foo<I, T> {
    bar: Vec<T>,
    _marker: PhantomData<I>,
}

impl <I, T> Foo<I, T> {
    fn new(v: Vec<T>) -> Foo<I, T> {
        Foo {
            bar: v,
            _marker: PhantomData,
        }
    }
    fn get(&self, index: &I) -> &T {
        // let index: usize = index.0; // I want to use this line.
        let index = 0; // This line is incorrect, but compiles.
        &self.bar[index]
    }
}

The generic newtype is guaranteed to be usize, like this:

pub struct Baz(pub usize);
pub struct Qux(pub usize);

This compiles and correctly enforces type checking.

fn main() {
    let index_b = Baz(1);
    let index_q = Qux(1);
    let data: Foo<Baz, i32> = Foo::new(vec![1,2,3]);
    assert_eq!(*data.get(&index_b), 2);

    // Type Checking Violation - Does not compile.
    // assert_eq!(*data.get(&index_q), 2);
}

However notice that the get() function is incorrect. How do you tell the compiler that I is a usize so the get() function will be correct?


Solution

  • You'll need to use some kind of trait. There's no existing constraint to verify that an I is actually a SomeType(T) to know that index.0 is valid.

    My immediate thought would be to implement Into on your newtype:

    #[derive(Copy, Clone)]
    pub struct Baz(pub usize);
    
    impl Into<usize> for Baz {
        fn into(self) -> usize {
            self.0
        }
    }
    

    And you would pass index by value into get:

    fn get(&self, index: I) -> &T
    where
        I: Into<usize>
    {
        &self.bar[index.into()]
    }
    

    Other options would be Deref or AsRef or it may make sense to make your own FooIndex trait.


    For AsRef:

    fn get(&self, index: &I) -> &T
    where
        I: AsRef<usize>
    {
        &self.bar[*index.as_ref()]
    }
    

    And Deref:

    fn get(&self, index: &I) -> &T
    where
        I: Deref<Target=usize>
    {
        &self.bar[**index]
    }