My goal is to retrieve an element from a tuple given the type of that element. For my use case, the types within the tuple are expected to be unique. The code I wrote below implements this behavior but I'm not too familiar with rust macros and I'm wondering if there's a "better" way or an existing crate that already does this for me.
struct Index0;
struct Index1;
struct Index2;
trait Element<T, I> {
fn get(&self) -> &T;
}
impl<T> Element<T, Index0> for T {
fn get(&self) -> &T {
self
}
}
macro_rules! impl_element {
($idx:tt, $idx_type:ty, $get:ident; $($tuple:ident),*) => {
impl<$($tuple),*> Element<$get, $idx_type> for ($($tuple),*)
where $get: Element<$get, Index0>
{
fn get(&self) -> &$get {
self.$idx.get()
}
}
}
}
impl_element!(0, Index0, A; A, B);
impl_element!(1, Index1, B; A, B);
impl_element!(0, Index0, A; A, B, C);
impl_element!(1, Index1, B; A, B, C);
impl_element!(2, Index2, C; A, B, C);
fn main() {
let a = (4.1, 2);
let b = ("foo", false, 5);
let x: &i32 = a.get();
let y: &bool = b.get();
let z: &&str = b.get();
assert_eq!(x, &2);
assert_eq!(y, &false);
assert_eq!(z, &"foo");
}
I learned some more things about macros and I was able to improve impl_element a bit. Now, it has the interface I was after but its implementation is not the greatest as it doesn't support arbitrary-sized tuples. Another thing I wanted to improve was the Element trait. Preferably, it would only have one type argument (i.e. Element<T>).
Interesting crate, thanks for the suggestion! I think the HCons::get method along with the Selector trait is pretty close to what I want. In fact, the Selector trait looks fairly similar to my Element trait. However, in my use case, I'm given a tuple and I can't seem to find a way to convert a tuple into an HList without specifying type arguments. Here's my closest attempt to replicate the above code given tuples.
#[macro_use]
extern crate frunk;
fn main() {
let a: Hlist![f32, i32] = From::from((4.1, 2));
let b: Hlist![&str, bool, i32] = From::from(("foo", false, 5));
let x: &i32 = a.get();
let y: &bool = b.get();
let z: &&str = b.get();
assert_eq!(x, &2);
assert_eq!(y, &false);
assert_eq!(z, &"foo");
}
Ah, yeah, interesting. Does this help? I'm not sure if it's possible to make it generic to tuple size, but at least you can let the compiler infer the types:
let a: Hlist![_, _] = From::from((4.1, 2));
let b: Hlist![_, _, _] = From::from(("foo", false, 5));
let x: &i32 = a.get();
let y: &bool = b.get();
let z: &&str = b.get();
assert_eq!(x, &2);
assert_eq!(y, &false);
assert_eq!(z, &"foo");
use frunk::generic::Generic;
fn main() {
let a = Generic::into((4.1, 2));
let b = Generic::into(("foo", false, 5));
let x: &i32 = a.get();
let y: &bool = b.get();
let z: &&str = b.get();
assert_eq!(x, &2);
assert_eq!(y, &false);
assert_eq!(z, &"foo");
}