mercredi 21 mars 2018

Rust reflection requies type annotation

#![feature(universal_impl_trait)]

use std::any::Any;
use std::marker::PhantomData;

trait Foo {
    type Item;
    fn get(&self) -> Self::Item;
}


#[derive(Clone, Debug)]
struct Bar<T: Clone>(T);
impl<T: Clone> Bar<T> { fn new(t: T) -> Self { Bar(t) } }
impl<T:'static + Clone> Foo for Bar<T> {
    type Item = T;
    fn get(&self) -> Self::Item {
        self.0.clone()
    }
}


#[derive(Clone, Debug)]
struct Baz<T: Clone,F: Clone>(T,F);
impl<T: Clone,F: Clone> Baz<T,F> { fn new(t: T, f:F) -> Self { Baz(t,f) } }
impl<T:'static + Clone, F:'static + Clone> Foo for Baz<T,F> {
    type Item = (T,F);
    fn get(&self) -> Self::Item {
        (self.0.clone(), self.1.clone())
    }
}



trait Get {
    type Item;
    fn idx(&self) -> usize;
}

struct GetBar<T> {id: usize, _t: PhantomData<T>}
impl<T> Get for GetBar<T> {
    type Item = T;
    fn idx(&self) -> usize { self.id }
}
impl<T> GetBar<T> {
    fn new(id: usize) -> Self {Self {id, _t: PhantomData} }
}

struct GetBaz<T,F> {id: usize, _t: PhantomData<T>, _f: PhantomData<F> }
impl<T,F> Get for GetBaz<T,F> {
    type Item = T;
    fn idx(&self) -> usize { self.id }
}
impl<T,F> GetBaz<T,F> {
    fn new(id: usize) -> Self { GetBaz {id, _t: PhantomData, _f: PhantomData} }
}




struct Qux {
    v: Vec<Box<Any>>,
}
impl Qux {
    fn new() -> Self {
        Qux {
            v: vec![],
        }
    }

    fn add_bar<T:'static + Clone>(&mut self, a: T) -> GetBar<T> {
        self.v.push(Box::new(Bar::new(a)) as Box<Any>);
        GetBar::new(self.v.len())
    }

    fn add_baz<T:'static + Clone, F:'static + Clone>(&mut self, a: T, b: F) -> GetBaz<T,F> {
        self.v.push(Box::new(Baz::new(a, b)) as Box<Any>);
        GetBaz::new(self.v.len())
    }

    fn get<T:'static + Clone, F: 'static + Clone>(&self, val: &'static impl Get) -> Option<T> {
        let node = &self.v[val.idx()];
        if let Some(foo) = node.downcast_ref::<Bar<T>>() {
            Some(foo.get())
        } else if let Some(foo) = node.downcast_ref::<Baz<T, F>>() {
            Some(foo.get().0)
        } else {
            None
        }
    }
}

fn main() {
    let mut qux = Qux::new();

    let a = qux.add_bar(1_i32);
    let b = qux.add_bar("1");
    let c = qux.add_baz(Bar::new('A'), Bar::new('B'));

    assert_eq!(qux.get(&a).unwrap(), 1);
    assert_eq!(qux.get(&b).unwrap(), "i");
    assert_eq!(qux.get(&c).unwrap(), Bar::new('A'));

}

Sorry for the long example but this is the best I can do. So I'm trying to reconcile the Rust static type system with dynamically dispatched trait objects. However, in many occasions I am unable to provide type annotations. What is the best way to achieve this? How can I dynamically supply type information to the trait object system?





Aucun commentaire:

Enregistrer un commentaire