Pull to refresh

Comments 8

Доступно. Но это всё-таки не столько проблема «замыканий», сколько времени жизни. Ну и, учитывая, что не каждый день в английской речи используешь слово closure — я статью открыл с интересом узнать о чём-то, чего не знаю :) А оказалось, что это closures.

Я бы вопрос о времени жизни переменных в ... всё время хочется назвать их лямбда-функциями ... в замыканиях развил в вопрос о времени жизни (lifetime, лайфтайм) в общем. Потому что первое прочтение rust book оставило у меня только необходимость аннотации лайфтаймов в функциях, возвращающих референсы.

А вот лайфтайм полей стуктуры, особенно, когда время жизни main() не равно времени жизни программы... В общем, на равне с макросами, лайфтаймы — тот синтаксис, которым очень боязно пользоваться.

Кстати, теперь понятней стало и смысл FnOnce - оно поглощает self, как раз этот объект замыкания.

Хорошая статья, спасибо.

Странно, что замыкания создаются так неявно.

В других частях языка наоборот, упор на прозрачность: сигнатуры функций прописывающие правила владения, возврат result вместо бросания ошибок, option вместо null-ов или «-1» и тому подобного.

Замыкания же, будто пришли прямиком из яваскрипта. Нельзя указать тип владения, нельзя прописать лайфтаймы, загадочные типы, котрые друг другу не равны. Их лишь подпёрли костылём в виде слова move.

Отчасти соглашусь с вами. Но все-таки пользоваться ими довольно просто. В Rust есть много подобных компромиссов. Скажем, в тех же сигнатурах функций есть специальный синтаксис для значений self и lifetime elision. Которые избавляют от синтаксического шума в наиболее популярных случаях использования.

Для замыканий, думаю, основная проблема в том, что они определяются в контексте выражения и из-за захвата неразрывно связаны с этим контекстом. Как пользователю в принципе записать подобный хитрый тип - совершенно непонятно.

Мне кажется, стоило бы пояснить, по каким именно правилам выполняется рассахаривание замыканий, потому что сейчас псевдокод берётся из воздуха. А правила не такие уж и сложные:


  • если замыкание объявлено без ключевого слова move, то оно захватывает внешние переменные по ссылке, если это возможно, и по значению в противном случае.
  • если замыкание объявлено с ключевым словом move, то оно безусловно захватывает переменные по значению.

Ошибка заключается в том, что move относят...

Мне кажется, стоило бы выделить не ошибку, а правильное значение из следующего предложения. Сейчас внимание акцентируется не на том, что стоило бы запоминать.

Хорошая статья, но мне кажется примеры получились слишком синтетические.
Было бы хорошо видеть либо замечание, что следующему коду:

fn new_closure(msg: String) -> impl Fn(usize) -> usize {
    move |x| msg.clone().into_bytes().len() * x
}

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

И код, скорее всего, должен выглядеть так:

fn new_closure(msg: String) -> impl Fn(usize) -> usize {
    move |x| msg.len() * x
}

Либо можно привести другой пример, где clone действительно имеет смысл, допустим:

fn new_closure(msg: String) -> impl Fn(&str) -> String {
    move |msg2| msg.clone() + msg2
}

в данном случае просто удалить clone не получится, не переписав код на format!("{msg}{msg2}") или что-то другое.

P.S. Статья полезная, просто хотелось напомнить, что примеры из статей, комментариев, обсуждений нужны не для для того, чтобы их использовать/копировать в неизменном виде

Sign up to leave a comment.

Articles