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