2017-03-01 1 views
1

J'essaye de travailler avec LLVM dans Rust en utilisant this crate. J'essaye de créer une structure de générateur de code pour tenir le contexte, le module, et le constructeur pour moi, mais quand j'essaye de compiler j'obtiens un message d'erreur qui indique c does not live long enough. Comment puis-je obtenir cela pour compiler, et pourquoi ne vit-il pas assez longtemps?La valeur ne vie pas assez longtemps quand mis en struct

code:

use llvm::*; 
use llvm::Attribute::*; 
pub struct CodeGen<'l> { 
    context: CBox<Context>, 
    builder: CSemiBox<'l, Builder>, 
    module: CSemiBox<'l, Module>, 
} 
impl<'l> CodeGen<'l> { 
    pub fn new() -> CodeGen<'l> { 
     let c = Context::new(); 
     let b = Builder::new(&c); 
     let m = Module::new("test", &c); 
     CodeGen { 
      context: c, 
      builder: b, 
      module: m, 
     } 
    } 
} 

message d'erreur complète:

error: `c` does not live long enough 
    --> src/codegen.rs:17:31 
    | 
17 |   let b = Builder::new(&c); 
    |        ^does not live long enough 
... 
24 |  } 
    |  - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32... 
    --> src/codegen.rs:15:33 
    | 
15 |  pub fn new() -> CodeGen<'l> { 
    |        ^

error: `c` does not live long enough 
    --> src/codegen.rs:18:38 
    | 
18 |   let m = Module::new("test", &c); 
    |         ^does not live long enough 
... 
24 |  } 
    |  - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32... 
    --> src/codegen.rs:15:33 
    | 
15 |  pub fn new() -> CodeGen<'l> { 
    |        ^

error: aborting due to 2 previous errors 
+0

Pourquoi downvote? – BookOwl

Répondre

2

Cela ressemble à une de ces situations où élision à vie rend les choses moins claires.

Voici le prototype de Builder::new:

pub fn new(context: &Context) -> CSemiBox<Builder> 

Ce qui pourrait vous faire penser que le CSemiBox n'a pas de relation avec la durée de vie context. Mais le definition de CSemiBox a un paramètre de durée de vie:

pub struct CSemiBox<'a, D> 

Si je comprends bien, lorsque le type de sortie d'une fonction (dans ce cas Builder::new) a un paramètre de durée de vie, il peut être élidé s'il n'y a qu'une seule entrée durée de vie. (Les règles d'élision à vie sont décrites dans the book et dans this question.) Dans ce cas, la durée de vie de sortie est considérée comme la même que la durée de vie d'entrée. Cela signifie que le prototype d'avant est en fait équivalent à ce qui suit:

pub fn new<'a>(context: &'a Context) -> CSemiBox<'a, Builder> 

J'espère que cela clarifie ce qui se passe: après Builder::new(&c), le CSemiBox contient une référence à la Context il a été créé à partir (b contient une référence à c) . Vous ne pouvez pas mettre b et c dans la même structure car le compilateur doit être en mesure de prouver que c survit b. Pour une explication plus complète, voir Why can't I store a value and a reference to that value in the same struct?

Il y a deux façons de gérer cela. (Vous ne pouvez pas utiliser Rc parce que vous ne contrôlez pas la caisse.)

  1. Ne pas stocker le Context dans la struct CodeGen. Vous êtes limité dans la façon dont vous pouvez structurer votre code, mais ce n'est pas nécessairement mauvais. Comme le Context est stocké sur le tas, vous pouvez utiliser unsafe pour que les références aient une durée de vie de 'static. Quelque chose comme l'extrait suivant devrait fonctionner, ce qui supprime l'annotation à vie de CodeGen. Si vous faites cela (comme à chaque fois que vous utilisez unsafe), vous prenez la responsabilité d'assurer la sécurité de l'interface exposée. Cela signifie, par exemple, CodeGen ne peut pas distribuer des références à builder et module, parce que cela pourrait divulguer une référence 'static à context.

    pub struct CodeGen { 
        context: CBox<Context>, 
        builder: CSemiBox<'static, Builder>, 
        module: CSemiBox<'static, Module>, 
    } 
    impl CodeGen { 
        pub fn new() -> CodeGen { 
         let c = Context::new(); // returns a CBox<Context> 
         let c_static_ref: &'static _ = unsafe { 
          let c_ptr = c.as_ptr() as *const _; // get the underlying heap pointer 
          &*c_ptr 
         }; 
         let b = Builder::new(c_static_ref); 
         let m = Module::new("test", c_static_ref); 
         CodeGen { 
          context: c, 
          builder: b, 
          module: m, 
         } 
        } 
    } 
    
+2

[Voir aussi cette question.] (Http://stackoverflow.com/q/32300132/234590) –

+0

@ FrancisGagné AHA! C'est celui que je voulais lier, mais je ne pouvais trouver [celui-ci] (http://stackoverflow.com/questions/20698384/lifetime-of-rust-structs-that-reference-each-other) en cherchant. Merci – trentcl

+0

Merci pour la bonne réponse! – BookOwl