2015-07-09 12 views
18

Ecco un programma Rust non valido (Rust versione 1.1) con una funzione che esegue una richiesta client HTTP e restituisce solo le intestazioni, lasciando cadere tutti gli altri campi nella risposta.Come spostare un campo da una struttura che implementa il tratto Drop?

extern crate hyper; 

fn just_the_headers() -> Result<hyper::header::Headers, hyper::error::Error> { 
    let c = hyper::client::Client::new(); 
    let result = c.get("http://www.example.com").send(); 
    match result { 
     Err(e) => Err(e), 
     Ok(response) => Ok(response.headers), 
    } 
} 

fn main() { 
    println!("{:?}", just_the_headers()); 
} 

Qui ci sono gli errori del compilatore:

main.rs:8:28: 8:44 error: cannot move out of type `hyper::client::response::Response`, which defines the `Drop` trait 
main.rs:8   Ok(response) => Ok(response.headers), 
           ^~~~~~~~~~~~~~~~ 
error: aborting due to previous error 

capisco perché il correttore prestito non accetta questo programma, cioè, che la funzione drop utilizzerà il response dopo che ha avuto il suo headers membro spostato.

La mia domanda è: come posso aggirare questo e avere ancora un buon codice Rust sicuro? So che posso fare una copia , via clone(), in questo modo:

Ok(response) => Ok(response.headers.clone()), 

Ma, venendo da C++, che sembra inefficiente. Perché copia quando uno spostamento è dovrebbe essere sufficiente? Mi immagino in C++ fare qualcosa di simile al seguente per forzare una chiamata a un costruttore mossa, se disponibile:

headers_to_return = std::move(response.headers); 

Esiste un modo di rinunciare alla copia a Rust e forzare un mossa, simile a C++?

risposta

18

È possibile utilizzare std::mem::replace() per scambiare il campo con un nuovo valore vuoto al fine di trasferire la proprietà a voi:

extern crate hyper; 

fn just_the_headers() -> Result<hyper::header::Headers, hyper::error::Error> { 
    let c = hyper::client::Client::new(); 
    let result = c.get("http://www.example.com").send(); 
    match result { 
     Err(e) => Err(e), 
     Ok(mut response) => Ok(std::mem::replace(&mut response.headers, hyper::header::Headers::new())), 
    } 
} 

fn main() { 
    println!("{:?}", just_the_headers()); 
} 

Qui, stiamo sostituendo response.headers con un nuovo insieme vuoto di intestazioni. replace() restituisce il valore che è stato memorizzato nel campo prima di essere sostituito.

+3

Potrebbe non valere nulla che 'std :: mem :: replace' in Rust sia * più o meno * cosa' std :: move' è in C++. Poiché l'origine e la destinazione devono essere valide per distruggere entrambi prima * e dopo * una mossa, C++ non si muove realmente, ma scambia. –

+2

Infatti, con la differenza che in C++, è la classe che decide come implementare lo spostamento (nel costruttore di movimento o nell'operatore di spostamento), mentre 'std :: mem :: replace' richiede al chiamante di fornire un valore adeguato . Infatti, 'std :: mem :: replace' è implementato in termini di [' std :: mem :: swap'] (http://doc.rust-lang.org/stable/std/mem/fn.swap .html). –

+2

Si potrebbe notare che Rust pone anche enfasi su "costrutti predefiniti" estremamente efficienti, per esempio né 'String :: new()' né 'Vec :: new()' allocano memoria, che è ciò che rende questo sostituto come efficiente come il C++ oltre a essere più sicuro. –

Problemi correlati