2015-11-30 13 views
5

Sto scrivendo un programma che scrive su un file e ruota il file su cui sta scrivendo ogni tanto. Quando controllo di ruotare il file, non riesco a cambiare il file dato che è stato preso in prestito dalla mia struct. Anche se I drop l'istanza della struct, non riesco a riacquistare la proprietà del file per rinominarlo. Ecco la mia example:"non può uscire dalla variabile perché è preso in prestito" durante la rotazione delle variabili

use std::fs::File; 
use std::io::{Write}; 
use std::mem::{drop}; 

pub struct FileStruct<W: Write> { 
    pub writer: Option<W>, 
} 

impl <W: Write> FileStruct<W> { 
    pub fn new(writer: W) -> FileStruct<W> { 
     FileStruct { 
      writer: Some(writer), 
     } 
    } 
} 

fn main() { 
    let mut file = File::create("tmp.txt").unwrap(); 
    let mut tmp = FileStruct::new(&mut file); 
    loop { 
     if true { //will be time based if check 
      drop(tmp); 
      drop(file); 
      file = File::create("tmp2.txt").unwrap(); 
      tmp = FileStruct::new(&mut file); 
     } 
     // write to file 
    } 
} 

so di poter arrivare a questo lavoro spostando la creazione del file nella chiamata new funzione FileStruct invece di avere una variabile intermedia, file, ma vorrei sapere perché questo metodo dove ho forzatamente cadere tutte le variabili in cui tutte le variabili che i riferimenti dovrebbero essere restituiti non funziona.

+0

Avete bisogno di 'tmp' al di fuori del if-block? Se non lo fai, puoi assicurarti che il prestito termini prima di riassegnare il 'file'. – fjh

+0

lo faccio. L'idea è che sto scrivendo i dati nel ciclo e il blocco if è quello di cambiare il file di output. Il blocco if è pensato per determinare se dovrei cambiare 'file'. –

risposta

6

Come the std::mem::drop documentation dice,

Anche se questo fa chiamare l'implementazione del ragionamento di Drop, non rilascerà alcun prende in prestito, come prende in prestito si basano su scope lessicale.

Quindi, anche se si chiama drop, file rimarrà in prestito comunque.

+0

Ecco alcune informazioni sui possibili ambiti non lessicali: https://github.com/rust-lang/rfcs/issues/811 –

2

L'eliminazione di tmp non "rilascia il prestito" di file perché il prestito è in ambito lessicale. È "attivo" a condizione che l'esecuzione del programma sia all'interno dello scope lessicale che contiene tmp anche se lo si elimina. Ciò che avresti voluto fare in futuro potrebbe essere se/una volta "gli ambiti non lessicali" sono supportati. Fino ad allora, è possibile farlo funzionare con RefCell:

use std::cell::RefCell; 
use std::io::{ self, Write }; 

/// wraps a reference to a RefCell<W> 
struct RefCellWriteRef<'a, W: 'a>(&'a RefCell<W>); 

/// implement Write for when W is Write 
impl<'a, W: Write + 'a> Write for RefCellWriteRef<'a, W> { 
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 
     let mut w = self.0.borrow_mut(); 
     w.write(buf) 
    } 
    fn flush(&mut self) -> io::Result<()> { 
     let mut w = self.0.borrow_mut(); 
     w.flush() 
    } 
} 

fn main() { 
    let file: RefCell<Vec<u8>> = RefCell::new(Vec::new()); 
    // use RefCellWriteRef(&file) instead of &mut file 
    let mut tmp = RefCellWriteRef(&file); 
    for iter in 0..10 { 
     if iter == 5 { 
      drop(tmp); 
      file.borrow_mut().clear(); // like opening a new file 
      tmp = RefCellWriteRef(&file); 
     } 
     tmp.write(b"foo").unwrap(); 
    } 
    drop(tmp); 
    println!("{}", file.borrow().len()); // should print 15 
} 

Il trucco è che, dato un riferimento condiviso a un RefCell<T> si può (finalmente) avere una &mut T via borrow_mut(). Il controllore del prestito in fase di compilazione è soddisfatto perché usiamo solo un riferimento condiviso sulla superficie ed è OK condividere lo file in questo modo. L'aliasing mutabile viene evitato controllando in fase di esecuzione se lo T interno è già stato mutuato in prestito.

Problemi correlati