2017-08-23 5 views
3

J'ai eu récemment une erreur qui a été tout simplement résolu en changeantQuelle est la différence entre '& self' et '&' a self '?

impl<'a> Foo<'a> { 
    fn foo(&'a self, path: &str) -> Boo<'a> { /* */ } 
} 

à

impl<'a> Foo<'a> { 
    fn foo(&self, path: &str) -> Boo { /* */ } 
} 

qui n'a pas de sens selon ma compréhension, je pensais que la deuxième version est exactement le même que le premier avec une élision à vie appliquée.


Dans le cas où nous introduisons une nouvelle vie pour la méthode, cela semble être le cas selon cet exemple de la nomicon.

fn get_mut(&mut self) -> &mut T;      // elided 
fn get_mut<'a>(&'a mut self) -> &'a mut T;    // expanded 

Alors, quelles sont les différences entre ceci et mon premier code coupé.

Répondre

4

vie 'a dans fn foo(&'a self, ...) ... est défini pour impl<'a>, c'est-ce est la même pour tous les appels foo.

La durée de vie 'a dans fn get_mut<'a>(&'a mut self) ... est définie pour la fonction. Différents appels de get_mut peuvent avoir des valeurs différentes pour 'a.

Votre code

impl<'a> Foo<'a> { 
    fn foo(&'a self, path: &str) -> Boo<'a> { /* */ } 
} 

n'est pas l'expansion de la vie élision. Ce code lie la durée de vie de l'emprunt &'a self à la durée de vie de la structure Foo<'a>. Si Foo<'a> est invariant sur 'a, alors self devrait rester emprunté aussi longtemps que 'a.

extension correcte de la vie élision est

impl<'a> Foo<'a> { 
    fn foo<'b>(&'b self, path: &str) -> Boo<'b> { /* */ } 
} 

Ce code ne dépend pas de la variance de la structure Foo pour être en mesure d'emprunter self pour des durées de vie plus courte.

Exemple de différences entre les structures variant et invariante.

use std::cell::Cell; 

struct Variant<'a>(&'a u32); 

struct Invariant<'a>(Cell<&'a u32>); 

impl<'a> Variant<'a> { 
    fn foo(&'a self) -> &'a u32 { 
     self.0 
    } 
} 

impl<'a> Invariant<'a> { 
    fn foo(&'a self) -> &'a u32 { 
     self.0.get() 
    } 
} 

fn main() { 
    let val = 0; 
    let mut variant = Variant(&val);// variant: Variant<'long> 
    let mut invariant = Invariant(Cell::new(&val));// invariant: Invariant<'long> 
    { 
     let r = variant.foo(); 
     // Pseudocode to explain what happens here 
     // let r: &'short u32 = Variant::<'short>::foo(&'short variant); 
     // Borrow of `variant` ends here, as it was borrowed for `'short` lifetime 

     // Compiler can do this conversion, because `Variant<'long>` is 
     // subtype of Variant<'short> and `&T` is variant over `T` 
     // thus `variant` of type `Variant<'long>` can be passed into the function 
     // Variant::<'short>::foo(&'short Variant<'short>) 
    } 
    // variant is not borrowed here 
    variant = Variant(&val); 

    { 
     let r = invariant.foo(); 
     // compiler can't shorten lifetime of `Invariant` 
     // thus `invariant` is borrowed for `'long` lifetime 
    } 
    // Error. invariant is still borrowed here 
    //invariant = Invariant(Cell::new(&val)); 
} 

Playground link

+0

deuxième variante indique au compilateur que '' Boo <'b> devrait vivre aussi longtemps que 'emprunter et 'b self'. C'est quand 'Boo <'b>' n'est plus dans la portée lexicale où il a été pris, 'self' n'est plus emprunté. – red75prime

+0

La première variante lie la durée de vie d'emprunter '& 'un soi' à la durée de vie de la structure' Foo <'a> '. Si 'Foo <'a>' est [invariant] (https://doc.rust-lang.org/nomicon/subtyping.html#variance) sur '' a ', cela signifie que 'self' devrait rester emprunté tant que'' a'. – red75prime

+0

J'ai ajouté un peu d'explication pourquoi la variance de 'Foo' affecte votre code. – red75prime