Как перенести одно поле из структуры, которая реализует черту Drop?

Вот некорректная программа Rust (версия Rust версии 1.1) с функцией, которая выполняет запрос HTTP-клиента и возвращает только заголовки, отбрасывая все остальные поля в ответе.

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());
}

Вот ошибки компилятора:

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

Я понимаю, почему средство проверки займа не принимает эту программу, т.е. что функция drop будет использовать response после того, как ее элемент headers будет перемещен.

Мой вопрос: как я могу обойти это и все еще иметь хороший безопасный код Rust? Я знаю, что могу сделать копию через clone(), например:

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

Но, исходя из С++, это кажется неэффективным. Зачем копировать, когда нужно двигаться? Я представляю себе, что на С++ вы делаете что-то вроде следующего, чтобы принудительно вызвать конструктор перемещения, если он доступен:

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

Есть ли способ отказаться от копии в Rust и вместо этого выполнить перемещение, подобное С++?

Ответы

Ответ 1

Вы можете использовать std::mem::replace() для замены поля новым пустым значением, чтобы передать вам право собственности:

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());
}

Здесь мы заменяем response.headers новым пустым набором заголовков. replace() возвращает значение, которое было сохранено в поле, прежде чем мы его заменили.