Почему эту переменную стека не нужно перемещать в замыкание? - PullRequest
2 голосов
/ 28 октября 2019

Я хотел бы понять, почему внешняя переменная стека data в приведенном ниже примере не требует использования ключевого слова move с закрытием.

Пример взят из Ящик скручивания , а в документации указано, что время жизни write_function взято из переменной transfer, и из-за этого замыкание может обращаться к переменной стека data, не перемещая ее. .

Вот цитата из документации:

Обратите внимание, что время жизни, связанное с этой функцией, является статическим, но это часто слишком ограничительно. Чтобы использовать данные стека, можно вызвать метод передачи и затем использовать функцию write_function для настройки обратного вызова, который может ссылаться на локальные данные стека.

См .: https://docs.rs/curl/0.4.6/curl/easy/struct.Easy.html#method.write_function

use curl::easy::Easy;

let mut data = Vec::new();
let mut handle = Easy::new();
handle.url("https://www.rust-lang.org/").unwrap();
{
    let mut transfer = handle.transfer();
    transfer.write_function(|new_data| {
        data.extend_from_slice(new_data);
        Ok(new_data.len())
    }).unwrap();
    transfer.perform().unwrap();
}
println!("{:?}", data);

Почемуэта работа?

В качестве альтернативы, если я пытаюсь использовать write_function из handle напрямую, то получаю ошибку заимствования, что data было перемещено.

Вотпример, который не работает, и я понимаю, почему это не работает. Я запутался, почему вышесказанное работает вместо этого.

use curl::easy::Easy;

let mut data = Vec::new();
let mut handle = Easy::new();
handle.url("https://www.rust-lang.org/").unwrap();
handle.write_function(move |new_data| {
   data.extend_from_slice(new_data);
   Ok(new_data.len())
}).unwrap();
handle.perform().unwrap();
println!("{:?}", data);
//               ^^^^ error because it was moved

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Указанная вами документация curl основана на методе curl::easy::Easy::write_function. Как описано, эта функция принимает замыкание с ограничением времени жизни 'static, другими словами, оно должно продолжаться на протяжении всей программы.

Это означает, что любые значения, заимствованные таким замыканием, никогда не могут быть возвращены, потому что замыкание никогда не выходит за рамки.

Это связано с тем, что библиотека curl rust оборачивает библиотеку curl C, а библиотека C позволяет вызывающей стороне регистрировать функции обратного вызова для различных событий. Компилятор ржавчины не может отследить время жизни замыканий после того, как они были переданы в качестве функций обратного вызова в библиотеку C, поэтому единственным безопасным временем жизни является 'static.

Чтобы обойти это, curl модуль предоставляет объект Transfer, для которого вы можете зарегистрировать замыкания, которые имеют время жизни менее 'static. Transfer объект заботится о регистрации и отмене регистрации обратных вызовов в базовой библиотеке C с учетом их времени жизни.

Узнав это, рассмотрите пример кода:

{
    let mut transfer = handle.transfer();
    transfer.write_function(|new_data| {
        data.extend_from_slice(new_data);
        Ok(new_data.len())
    }).unwrap();
    transfer.perform().unwrap();
}

ЗдесьЗатвор непостоянно занимает data. Закрытие передается в transfer.write_function, что требует, чтобы оно длилось столько же, сколько и сам объект передачи. Следовательно, data заимствовано до конца блока, в котором было объявлено transfer.

1 голос
/ 28 октября 2019

В вашем первом примере ржавчина проходит &mut data через стек и продолжает позволять <main> владеть data.

Во втором примере ржавчина дает handle.write_function(move |new_data| { владение data через ключевое слово move, затем внутренняя часть записывает в data и опускается в конце области действия }).unwrap();, затем в нижней части вы пытаетесь снова прочитать data через println!, что запрашивает ржавчину длянайдите то, что вы уже просили, чтобы его уронили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...