2017-09-21 10 views
0

Je mis en place un Tokio Future qui a la logique de haut niveau suivant:Ne peut pas déduire une durée de vie appropriée: Emprunts tampons mutables à terme

  1. Faire un appel à une fonction recv_dgram. Cela devrait retourner un Future qui est prêt quand un petit datagramme est reçu.
  2. Lorsque le Future est prêt, conservez le petit datagramme.
  3. Si nous avons suffisamment de petits datagrammes pour reconstruire un grand datagramme, réglez le Future comme prêt et renvoyez le datagramme reconstruit. Sinon, revenez à l'étape 1.

J'ai un problème de durée de vie que je n'arrive pas à comprendre. J'ai créé un morceau de code simplifié autonome pour le démontrer.

Dans le code ci-dessous, RecvMsg est le Future qui est prêt lorsque suffisamment de petits datagrammes ont été reçus. recv_dgram est une fonction qui renvoie un Future qui est prêt lorsqu'un petit datagramme est arrivé.

Je suis en train de compiler le code suivant (playground):

extern crate futures; 
use self::futures::{Future, Poll, Async}; 

struct RecvMsg<'d,R> 
where 
    R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>, 
{ 
    recv_dgram: R, 
    temp_buff: Vec<u8>, 
    opt_read_future: Option<Box<Future<Item=&'d mut [u8], Error=()> + 'd>>, 
} 

impl<'d,R> Future for RecvMsg<'d,R> 
where 
    R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>, 
{ 
    type Item =(); 
    type Error =(); 

    fn poll(&mut self) -> Poll<Self::Item,()> { 

     // Obtain a future datagram, 
     let mut fdgram = (self.recv_dgram)(&mut self.temp_buff); 
     // Code compiles if this line is commented out: 
     self.opt_read_future = Some(fdgram); 
     return Ok(Async::NotReady); 
    } 
} 

fn main() {} 

Ceci est le message d'erreur que je reçois:

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements 
    --> src/main.rs:25:44 
    | 
25 |   let mut fdgram = (self.recv_dgram)(&mut self.temp_buff); 
    |           ^^^^^^^^^^^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 22:5... 
    --> src/main.rs:22:5 
    | 
22 |/ fn poll(&mut self) -> Poll<Self::Item,()> { 
23 | | 
24 | |   // Obtain a future datagram, 
25 | |   let mut fdgram = (self.recv_dgram)(&mut self.temp_buff); 
... | 
28 | |   return Ok(Async::NotReady); 
29 | |  } 
    | |_____^ 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:25:44 
    | 
25 |   let mut fdgram = (self.recv_dgram)(&mut self.temp_buff); 
    |           ^^^^^^^^^^^^^^^^^^^ 
note: but, the lifetime must be valid for the lifetime 'd as defined on the impl at 13:1... 
    --> src/main.rs:13:1 
    | 
13 |/impl<'d,R> Future for RecvMsg<'d,R> 
14 | | where 
15 | |  R: for <'r> FnMut(&'r mut [u8]) -> Box<Future<Item=&'r mut [u8], Error=()> + 'r>, 
16 | | { 
... | 
29 | |  } 
30 | | } 
    | |_^ 
note: ...so that expression is assignable (expected std::option::Option<std::boxed::Box<futures::Future<Error=(), Item=&'d mut [u8]> + 'd>>, found std::option::Option<std::boxed::Box<futures::Future<Error=(), Item=&mut [u8]>>>) 
    --> src/main.rs:27:32 
    | 
27 |   self.opt_read_future = Some(fdgram); 
    |        ^^^^^^^^^^^^ 

J'ai quelques idées sur ce qui pourrait se tromper. Je sais que si je commente la ligne:

reading_state.opt_read_future = Some(fdgram); 

Le code compile avec succès. En outre, je pense que le fait que reading_state.temp_buff est utilisé ici comme argument:

let mut fdgram = (reading_state.frag_msg_receiver.recv_dgram)(
       &mut reading_state.temp_buff); 

a quelque chose à voir avec le problème. (Voir aussi Why can't I store a value and a reference to that value in the same struct?)

+2

Oui, il est (https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct). 'opt_read_future' contient une référence à' temp_buf'. – red75prime

+0

@ red75prime: En lisant votre commentaire, j'ai essayé de simplifier le code encore plus pour trouver le problème de base. Je me rends compte que j'ai à la fois 'temp_buff' et une référence à son contenu sur la même structure, mais' temp_buff' est un vecteur et son contenu est sur le tas. Pourquoi cela serait-il un problème? Je n'arrive pas à comprendre ce qui pourrait mal tourner ici. En outre, avez-vous une idée pour une solution de contournement? Où dois-je mettre 'temp_buff'? – real

Répondre

0

Les types implémentant Future mais n'ayant pas de 'static durée de vie sont assez inutiles, car la boucle d'événements ne fonctionnera pas être capable de les exécuter.

Aussi je pense que les durées de vie dans un Future::Item sont mauvaises; Je ne vois pas comment le système de type pourrait vous aider à déterminer dans le flux de contrôle quand le futur s'est réellement terminé pour libérer la durée de vie. Donc, vous voulez probablement R: FnMut(Vec<u8>) -> Box<Future<Item=Vec<u8>, Error=()>> à la place pour la fonction recv_dgram. Il recevra un tampon auquel il est supposé ajouter de nouvelles données reçues (il pourrait utiliser reserve() et remplir le buffer "dangereux" + set_len() pour l'optimisation). Vous pouvez également intégrer Vec dans un type personnalisé pour appliquer la sémantique "append-only".

Maintenant, vous devriez réaliser que la mutabilité ne va pas beaucoup aider cette fonction - vous pouvez l'appeler 10 fois de suite sans attendre que le futur soit terminé. Au lieu de cela, vous voulez probablement passer un état explicite. permet également d'éviter la boxe, et accepter tout résultat convertible avec IntoFuture:

// `S` is the state, `F` something convertible to a future `F::Future`. 
pub struct RecvMsg<R, F, S> 
where 
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>, 
    R: FnMut(S, Vec<u8>) -> F, 
{ 
    recv_dgram: R, 
    opt_read_future: Option<F::Future>, 
} 

L'opération de lecture en attente devient Option<F::Future>, et la vie du tampon pour cette opération en attente.

Cette opération de lecture en attente doit être interrogée chaque fois que votre wrapper est interrogé (et que vous n'avez pas encore terminé complètement)!

Dans l'ensemble, il pourrait ressembler à ceci: [? Pourquoi ne puis-je stocker une valeur et une référence à cette valeur dans le même struct]

Playground

extern crate futures; 
use self::futures::{IntoFuture,Future, Poll, Async}; 

pub struct RecvMsg<R, F, S> 
where 
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>, 
    R: FnMut(S, Vec<u8>) -> F, 
{ 
    recv_dgram: R, 
    pending: Option<F::Future>, 
} 

impl<R, F, S> RecvMsg <R, F, S> 
where 
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>, 
    R: FnMut(S, Vec<u8>) -> F, 
{ 
    pub fn new(mut recv_dgram: R, initial_state: S) -> Self { 
     let start = recv_dgram(initial_state, Vec::new()).into_future(); 
     RecvMsg{ 
      recv_dgram: recv_dgram, 
      pending: Some(start) 
     } 
    } 
} 

impl<R, F, S> Future for RecvMsg <R, F, S> 
where 
    F: IntoFuture<Item=(S, Vec<u8>), Error=()>, 
    R: FnMut(S, Vec<u8>) -> F, 
{ 
    type Item = Vec<u8>; 
    type Error =(); 

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> { 
     match self.pending.take() { 
      Some(mut p) => { 
       match p.poll()? { 
        Async::Ready((state, buf)) => { 
         if buf.len() > 1024 { 
          // enough data 
          Ok(Async::Ready(buf)) 
         } else { 
          // not enough data, read more 
          let next = (self.recv_dgram)(state, buf).into_future(); 
          self.pending = Some(next); 
          Ok(Async::NotReady) 
         } 
        }, 
        Async::NotReady => { 
         // still waiting for more data 
         self.pending = Some(p); 
         Ok(Async::NotReady) 
        }, 
       } 
      }, 
      None => Ok(Async::NotReady), // already completed 
     } 
    } 
}