2016-09-09 1 views
4

J'ai un trait Widget param etr sur un type de contexte:Déclarant un vecteur de trait des objets avec un paramètre de vie

trait Widget<C> { 
    fn f<'a>(&self, ctx: &'a mut C); 
} 

Certains widgets dont les types contexte sont les mêmes, mais qui contiennent des références si sont paramétrées:

struct Ctxt<'c> { 
    data: &'c u32, 
} 

struct W1 {} 
struct W2 {} 

impl<'c> Widget<Ctxt<'c>> for W1 { // and W2 
    fn f<'a>(&self, ctx: &'a mut Ctxt<'c>) { 
     unimplemented!() 
    } 
} 

J'ai un multi-widget qui veut stocker plusieurs d'entre eux:

struct WV { 
    widgets: Vec<Box<Widget<Ctxt<????>>>>, 
} 

impl<'c> Widget<Ctxt<'c>> for WV { 
    fn f<'a>(&self, ctx: &'a mut Ctxt<'c>) { 
     for w in &self.widgets { 
      w.f(ctx); 
     } 
    } 
} 

Il semble que j'ai besoin d'un Vec<Box<Widget<for<'c> Ctxt<'c>>>>; mais tu ne peux pas faire ça! Vous pouvez également spécifier que la durée de vie dans la définition de f:

impl Widget<Ctxt> for W { 
    fn f<'a, 'c>(&self, ctx: &'a mut Ctxt<'c>) { 
     unimplemented!() 
    } 
} 

Cela ne fonctionne pas non plus (paramètre manquant à vie pour Ctxt).

Le but du contexte est de passer une référence mutable à quelque chose de longue durée qui est seulement nécessaire pendant f; Je ne peux pas enregistrer la référence &mut dans W1 etc.

Comment puis-je stocker plusieurs implémenteurs de l'attribut, qui permettent de passer dans un contexte contenant des références?

+2

Je suis sûr qu'il me manque quelque chose, mais pourquoi ne pas utiliser 'struct WV <'c> {widgets: Vec >>>}'? Toutes les vies doivent être connues statiquement et cela les unifiera toutes. – Shepmaster

+0

La durée de vie n'est pas connue jusqu'à l'appel de 'f' lorsque vous avez un contexte à passer. Actuellement (sans' WV') je stocke juste un 'W1' qui n'a pas de paramètres de durée, et quand call' w1.f () 'le trait est effectivement spécialisé pour' Ctxt <'c> 'à ce moment. La prochaine fois que 'w1.f()' est appelé, il peut être sur 'Widget >'; mais le 'w1' est le même. La création ne se produit pas dans la même portée que l'appel de méthode. –

Répondre

3

Après une nuit de sommeil, je pense avoir une réponse. Je peux reporter la sélection de la durée de vie Ctxt par indirecting grâce à un nouveau trait CtxtWidget et impl<'c> Widget<Ctxt<'c>> pour le nouveau trait:

trait Widget<C> { 
    fn f<'a>(&self, ctx: &'a mut C); 
} 

struct W1 {} 
struct W2 {} 
struct Ctxt<'c> { 
    data: &'c u32, 
} 

trait CtxtWidget { 
    fn ctxt_f<'a, 'c>(&self, ctx: &'a mut Ctxt<'c>); 
} 

impl CtxtWidget for W1 { 
    fn ctxt_f<'a, 'c>(&self, ctx: &'a mut Ctxt<'c>) { 
     unimplemented!() 
    } 
} 
impl CtxtWidget for W2 { 
    fn ctxt_f<'a, 'c>(&self, ctx: &'a mut Ctxt<'c>) { 
     unimplemented!() 
    } 
} 

impl<'c> Widget<Ctxt<'c>> for Box<CtxtWidget> { 
    fn f<'a>(&self, ctx: &'a mut Ctxt<'c>) { 
     self.ctxt_f(ctx); 
    } 
} 

struct WV { 
    pub widgets: Vec<Box<CtxtWidget>>, 
} 

fn main() { 
    let mut wv = WV{widgets: Vec::new()}; 
    wv.widgets.push(Box::new(W1{})); 
    wv.widgets.push(Box::new(W2{})); 
    let u = 65u32; 
    let mut ctxt = Ctxt{data: &u}; 
    for widget in &wv.widgets { 
     widget.f(&mut ctxt); 
    } 
} 

(playground)

En effet CtxtWidget est à peu près équivalent à for<'c> Widget<Ctxt<'c>>.

Je serais toujours intéressé par d'autres solutions (y compris les changements intrusifs s'il y a une meilleure façon de le faire).