2017-09-26 15 views
1

J'ai un problème avec les durées de vie de Cell (UnsafeCell/RefCell/...). ce code de ma compréhension devrait compiler:Pourquoi les cellules sont-elles spéciales pour le vérificateur d'emprunt?

fn test1<'a, 'b: 'a>(x: Cell<&'b u32>) { 
    let x2: Cell<&'a u32> = x; 
} 

Mais il produit une erreur:

error[E0308]: mismatched types 
--> src/main.rs:4:29 
    | 
4 |  let x2: Cell<&'a u32> = x; 
    |       ^lifetime mismatch 
    | 
    = note: expected type `std::cell::Cell<&'a u32>` 
      found type `std::cell::Cell<&'b u32>` 
note: the lifetime 'a as defined on the function body at 3:1... 
--> src/main.rs:3:1 
    | 
3 |/fn test1<'a, 'b: 'a>(x: Cell<&'b u32>) { 
4 | |  let x2: Cell<&'a u32> = x; 
5 | | } 
    | |_^ 
note: ...does not necessarily outlive the lifetime 'b as defined on the function body at 3:1 
--> src/main.rs:3:1 
    | 
3 |/fn test1<'a, 'b: 'a>(x: Cell<&'b u32>) { 
4 | |  let x2: Cell<&'a u32> = x; 
5 | | } 
    | |_^ 

Je pense : à <> n'est pas opérateur bien connu, mais je l'ai trouvé dans certains RFC en essayant de résoudre mon problème.

Je devrais être en mesure de faire un Cell avec une durée de vie plus courte sur une plus longue. Quand je remplace le type Cell par un wrapper fictif tout fonctionne bien, donc de mes expériences il semble que Cell (UnsafeCell etc.) est traité spécialement quand il s'agit de durées de référence.

Ce n'est pas mon problème original. Je voulais avoir un état partagé entre plusieurs structures - une structure principale avec RefCell et des structures d'enfants avec référence à RefCell mais je ne peux pas obtenir le vérificateur d'emprunt heureux sans emprunter soi-même pour la durée de vie de l'objet entier. Voir:

struct A<'a> { 
    x: RefCell<&'a u32>, 
} 

impl<'a> A<'a> { 
    fn new(x: &'a u32) -> A<'a> { 
     A { x: RefCell::new(x) } 
    } 

    fn foo(&self) -> B<'a> { 
     B { x: &self.x } 
    } 
} 

struct B<'a> { 
    x: &'a RefCell<&'a u32>, 
} 

Si j'ajoute vie 'a-self dans foo, il compile, mais échoue pour ce code:

let fs = A::new(&x); 
{ 
    fs.foo(); 
} 
let fs2 = fs; 

Erreur: erreur [E0505]: ne peut pas sortir de fs parce qu'il est emprunté

Une autre méthode permet-elle de mettre en œuvre un état partagé entre des objets? J'utilise un seul thread donc theres aucun problème de synchronisation pour le moment.

+1

Voir https://doc.rust-lang.org/nomicon/subtyping.html pour une explication des raisons pour lesquelles il serait dangereux de laisser 'cellulaire 'être variante sur le type stocké. – interjay

+1

En résumé: 'Cell ' est invariant par rapport à 'T' car' Cell' peut être muté quand il est stocké dans un '&' -ref, et permettre la variance signifierait que vous pourriez stocker une référence avec une durée de vie plus courte dans la cellule ('let x2: & Cell <&'a u32> = & x; x2.set (quelquechoseWithLifetimeA); x.get() // renvoie & 'b T qui ne vit vraiment que pour' a') –

+0

Merci de m'indiquer les matériaux et de fournir des exemples de comment la variance fonctionne! Cela m'a aidé à comprendre mr. emprunter vérificateur et trouver une solution à mon problème. Je ne m'attendais pas à trouver une résolution si rapidement :) – Rafalh

Répondre

3

Comme expliqué dans les commentaires, mon problème a été causé par Cell type invariance. J'ai réussi à résoudre mon problème original en utilisant deux durées de vie au lieu d'utiliser la même durée de vie partout. Maintenant &self est emprunté à foo pour une durée de vie plus courte que 'a:

struct A<'a> { 
    x: RefCell<&'a u32>, 
} 

impl <'a> A<'a> { 
    fn new(x: &'a u32) -> A<'a> { 
     A { 
      x: RefCell::new(x), 
     } 
    } 

    fn foo<'b>(&'b self) -> B<'b, 'a> { 
     B { 
      x: &self.x, 
     } 
    } 
} 

struct B<'b, 'a: 'b> { 
    x: &'b RefCell<&'a u32>, 
}