2016-09-14 1 views
-2

Voici le code:paramètre de vie identique pour tous les champs d'une struct

#[derive(Debug)] 
struct Foo { 
    f: i32, 
} 

#[derive(Debug)] 
struct Bar<'a> { 
    bar1: &'a Foo, 
    bar2: &'a Foo, 
} 

#[allow(unused_variables)] 
fn make_bar<'a>(foo1: &'a Foo, foo2: &'a Foo) -> Bar<'a> { 
    Bar { 
     bar1: foo1, 
     bar2: foo2, 
    } 
} 

fn extract_bar2<'a>(foo: &'a Foo) -> &'a Foo { 
    let foo1 = Foo { f: 22 }; 
    let foo2 = make_bar(&foo, &foo1).bar1; 
    foo2 
} 

fn main() { 
    let foo = Foo { f: 11 }; 
    let foo1 = extract_bar2(&foo); 
    println!("foo1: {:?}", foo1); 
} 

Cela donne une erreur:

error: `foo1` does not live long enough 
    --> src/main.rs:23:32 
    |> 
23 |>  let foo2 = make_bar(&foo, &foo1).bar1; 
    |>        ^^^^ 
note: reference must be valid for the lifetime 'a as defined on the block at 21:45... 
    --> src/main.rs:21:46 
    |> 
21 |> fn extract_bar2<'a>(foo: &'a Foo) -> &'a Foo { 
    |>           ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 22:29 
    --> src/main.rs:22:30 
    |> 
22 |>  let foo1 = Foo { f: 22 }; 
    |>       ^

La question fondamentale est: Qu'est-ce qu'un paramètre à vie signifie réellement dans la contexte d'une structure?

Plus précisément: Quelles sont les conséquences d'avoir le même paramètre de durée de vie pour tous les champs d'une structure? Est-ce que leur durée de vie doit être exactement la même? Doivent-ils se chevaucher? Si oui, dans quelle mesure devraient-ils se chevaucher? Quelles sont les différences sémantiques et pratiques entre les deux structures suivantes?

struct Bar<'b> { 
    bar1: &'b Foo, 
    bar2: &'b Foo, 
} 
struct Bar<'a, 'b> { 
    bar1: &'a Foo, 
    bar2: &'b Foo, 
} 
+0

[Une question par question, s'il vous plaît] (http://meta.stackexchange.com/q/39223/281829). Votre deuxième moitié est [déjà répondu] (http://stackoverflow.com/q/29861388/155423). Le style de codage Rust est 'snake_case', pas' camelCase'. – Shepmaster

+0

Avez-vous regardé l'une des [91 questions existantes avec le même message d'erreur] (http://stackoverflow.com/search?q=%5Brust%5D+%22does+not+live+long+enough%22+is% 3Aq)? En quoi cette question diffère-t-elle de celles-ci? S'il vous plaît montrer [quel effort vous avez déjà fait pour résoudre le problème] (http://meta.stackoverflow.com/q/261592/155423). – Shepmaster

+0

Je ne demande pas comment éliminer cette erreur, je sais comment le faire. Une meilleure compréhension des paramètres de durée de vie est ce que je suis après. – qed

Répondre

0

What are the (semantic and practical) differences between the following two structs?

Il y a un petit exemple pour illustrer la différence:

#[derive(Debug)] 
struct Foo; 

#[derive(Debug)] 
struct Bar1<'b> { 
    foo1: &'b Foo, 
    foo2: &'b Foo, 
} 

#[derive(Debug)] 
struct Bar2<'a, 'b> { 
    foo1: &'a Foo, 
    foo2: &'b Foo, 
} 

fn main() {//'a --> 
    let foo1 = Foo; 
    let ref_foo1 = 
    {//'b --> 
     let foo2 = Foo; 
     //error: `foo2` does not live long enough 
     //replace the Bar1 with Bar2 in the row below to fix error 
     let bar = Bar1{foo1:&foo1, foo2:&foo2}; 
     bar.foo1 
    };//--> 'b 
    println!("ref_foo1={:?}", ref_foo1); 
}//--> 'a 

Le Bar1 tronque la durée de vie de ses membres à leur intersection. Donc, vous ne pouvez pas obtenir une référence à la foo1 avec la vie 'a de la struct Bar1. Vous obtenez la référence à vie 'b.

Je signale que le message d'erreur dans ce cas est un peu trompeur

0

je vais coller une réponse de reddit que je suis vraiment satisfait.

Le compilateur peut combiner des durées de vie en prenant leur intersection. Autrement dit, si vous avez 'a et 'b, alors il y a une intersection 'c de 'a et 'b, au cours de laquelle les deux durées de vie sont 'vivantes'. Je crois que c'est toujours le cas que cette intersection est égale à la plus courte de 'a et' b, à cause de la façon dont la délimitation fonctionne, mais peut-être que je me trompe.

Dans la pratique, cela signifie que, quand vous voyez un fn<'a>(x: &'a T, y: &'a U) -> &'a V, vous pouvez mettre dans un &'static T et un &'b U, et vous obtiendrez un &'b V, parce que l'intersection de 'static et 'b est 'b. Donc, pourquoi votre méthode fait-elle plier le compilateur? Parce que le compilateur, il ressemble un peu comme ça (ce n'est pas une syntaxe valide):

fn extract_bar2<'a>(foo: &'a Foo) -> &'a Foo { 
    'b: { 
     let foo1 = Foo { f: 22 }; 
     'c: { // The next line is wrong 
      let foo2: &'a Foo = make_bar<'a>(&'a foo, &'b foo1).bar1; 
      'd: { 
       return foo2; 
      } 
     } 
    } 
} 

J'ai fait les champs d'application plus explicite. Ce qui se produit? Le compilateur sait que foo2 doit avoir le type &'a Foo, car c'est ce que la fonction retourne. Donc, la barre retournée par make_bar doit avoir eu la vie 'a: nous ne pouvions pas en obtenir un &'a Foo sinon. Nous devons donc avoir appelé make_bar<'a>. Mais l'un des arguments est faux! foo2 n'a pas de durée de vie 'a, il a une durée de vie 'b, qui est dépassée par 'a. Si vous prenez l'intersection et faites ceci:

let foo2: &'b Foo = make_bar<'b>(&'a foo, &'b foo1).bar1; 

alors foo2 ne correspond pas au type de retour.Lorsque vous utilisez votre deuxième définition de Bar, le code fonctionnera, car bar1 et bar2 n'ont pas besoin d'avoir les mêmes durées de vie dans ce cas. Ainsi, votre deuxième définition de Bar est strictement plus flexible, mais en pratique vous avez rarement besoin de cette flexibilité supplémentaire, et les annotations supplémentaires à vie sont ennuyantes.

Crédit https://www.reddit.com/user/thiez