2014-11-06 8 views
5

ho problemi a capire perché questo codice non viene compilato:problema a vita con l'invio tratto

use std::cell::{Ref, RefCell}; 

struct St { 
    data: RefCell<uint> 
} 

impl St { 
    pub fn test(&self) -> Ref<uint> { 
     self.data.borrow() 
    } 
} 

// This code would compile without T constrained to be Send. 
fn func<T: Send>(_: &T) { 
} 

fn main() { 
    let s = St { data: RefCell::new(42) }; 

    { 
     let r7 = s.test(); 
     // Do not compile 
     func(&r7) 
    } 

    // Compile 
    func(&s); 
} 

Si dà il seguente errore:

bug.rs:21:18: 21:19 error: `s` does not live long enough 
bug.rs:21   let r7 = s.test(); 
         ^
note: reference must be valid for the static lifetime... 
bug.rs:17:11: 28:2 note: ...but borrowed value is only valid for the block at 17:10 
bug.rs:17 fn main() { 
bug.rs:18  let s = St { data: RefCell::new(42) }; 
bug.rs:19 
bug.rs:20  { 
bug.rs:21   let r7 = s.test(); 
bug.rs:22   // Do not compile 
      ... 

Il problema sembra essere nella funzione func() quando provo a limitare T per essere compatibile con il tratto Send. Senza questo vincolo questo codice viene compilato senza errori.

Qualcuno può spiegarmi qual è la ragione di questo comportamento?

risposta

10

Aggiornamento per ruggine 1,0

A Rust 1.0 e successivamente il codice nell'esempio (quando uint s sono sostituiti con un certo tipo esistente) non riesce con un altro errore:

% rustc test.rs 
test.rs:23:9: 23:13 error: the trait `core::marker::Sync` is not implemented for the type `core::cell::UnsafeCell<usize>` [E0277] 
test.rs:23   func(&r7) 
        ^~~~ 
test.rs:23:9: 23:13 help: run `rustc --explain E0277` to see a detailed explanation 
test.rs:23:9: 23:13 note: `core::cell::UnsafeCell<usize>` cannot be shared between threads safely 
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Cell<usize>` 
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::BorrowRef<'_>` 
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Ref<'_, i32>` 
test.rs:23:9: 23:13 note: required by `func` 

Questo è un pò difficile - un altro tratto, Sync, è apparso dal nulla.

Un tipo che implementa il tratto Send (sebbene la sua documentazione sia certamente carente fin d'ora) è qualcosa che può essere trasferito oltre i limiti delle attività. La maggior parte dei tipi sono Send, ma alcuni, come Rc e Weak, non sono Send poiché le istanze di tali tipi potrebbero condividere lo stato mutabile non sincronizzato e pertanto non sono sicuri da utilizzare da più thread.

Nelle versioni precedenti di Ruggine Send implicite 'static, quindi i riferimenti non erano Send. Da Rust 1.0, tuttavia, Send non implica più 'static, pertanto i riferimenti possono essere inviati attraverso i thread. Tuttavia, al fine di &T essere Send, T deve essere Sync: questo è richiesto dal seguente implementazione:

impl<'a, T> Send for &'a T where T: Sync + ?Sized 

Ma nel nostro caso non stiamo richiedendo che &T è Send, abbiamo solo bisogno che T è Send, quindi non dovrebbe davvero importare, giusto?

No. In effetti, ci sono ancora riferimenti, anche noi non li vediamo subito. Ricorda che, per un tipo che deve essere Send, ogni componente deve essere Send, ovvero, ogni campo di una struttura e ogni parte di ogni variante enum di un enum deve essere Send per questa struttura/enum da Send. core::cell::Ref internamente contains un'istanza di struct BorrowRef, che a sua volta contains un riferimento a Cell<BorrowFlag>. Ed ecco dove Sync proviene da: in ordine o &Cell<BorrowFlag> per essere Send, Cell<BorrowFlag> deve essere Sync; tuttavia, non è e non può essere Sync perché fornisce una mutabilità interna non sincronizzata. Questa è la vera causa dell'errore.

+0

Ottima risposta, grazie, ora capisco! – user3762625

2

Secondo the Rust reference (sottolineatura mia):

Send : Types of this kind can be safely sent between tasks. This kind includes scalars, boxes, procs, and structural types containing only other owned types. All Send types are 'static .

In effetti, se si invia qualcosa a un altro compito, è necessario garantire che non sarà distrutto prima quest'altro compito è finito di usarlo, così non può essere di proprietà del compito corrente.

Ci sono due modi per garantire che:

  • avere questo oggetto che viene interamente di proprietà (in pratica, tutti i membri della vostra struct vengono inviati oltre)
  • avere il vostro oggetto sia nella memoria statica

Quindi, richiedendo che l'argomento della funzione di essere Send, si richiedono r7 essere 'static, ma non può sopravvivere s (come è un riferimento al con RefCell tende), che non è 'static come è definito nel vostro principale.

Più in generale, quando si scrive

fn foo<T: 'a>(bar: T); 

Si richiede T essere o:

  • Un tipo con tutti i suoi argomenti a vita essendo 'a o più (o non avendo argomenti)
  • un &'a riferimento a un tipo stesso è 'a (ed è possibile recurse a queste condizioni)

E come abbiamo visto, T: Send implica T: 'static.

+0

In realtà, ci sono altri modi per assicurare che la vita sia abbastanza lunga. Ad esempio, in un approccio fork-join, invece di garantire che la durata dei dati sia "statica" in modo che qualsiasi attività possa utilizzarla in sicurezza, si garantisce che la durata dell'attività non superi la durata dei dati. La ruggine non fornisce ancora un fork-join, per quanto ne so io. –