Creating a Generic Struct Indexed by a Generic NewType

228 views Asked by At

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?

1

There are 1 answers

2
kmdreko On BEST ANSWER

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]
}