Можно ли использовать FnMut в переменной, которая не является изменяемой?

Функция принимает параметр типа FnMut, то есть этот параметр может изменять своё состояние. Почему тогда в сигнатуре функции event_handler не объявлен как mut? Документация утверждает, что мутабельный параметр необходим. В качестве примера приведена функция из winit:

```rust
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where
    F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
{
    self.event_loop.run(event_handler)
}
```

Как объяснить отсутствие явного указания на мутабельность параметра event_handler?
  • 6 сентября 2024 г. 10:13
Ответы на вопрос 3
В Rust, когда мы говорим о "мутабельности" в контексте замыканий и типов, таких как `FnMut`, важно понимать, что мутабельность касается состояния замыкания, а не того, как мы передаем сам аргумент в функцию.

Когда вы передаете замыкание в функцию, вы не изменяете саму переменную, в которую оно заключено (в данном случае `event_handler`), вы просто передаете ее как аргумент функции. Поэтому сигнатура функции не требует, чтобы сам параметр `event_handler` был объявлен как `mut`. Однако важно то, что когда вы вызываете это замыкание (то есть когда вы вызываете `event_handler(...)` внутри функции), это замыкание может изменять своё внутреннее состояние, поскольку оно реализует трейт `FnMut`.

Таким образом, подходящий пример, который вы привели, правильно демонстрирует, что потребность в изменяемости распространяется на то, как используется замыкание (то есть, на его внутреннее состояние), а не на сам аргумент функции. 

Пример использования `FnMut` выглядит так:

```rust
fn example<F>(mut handler: F)
where
    F: FnMut(),
{
    handler(); // Вызывая, мы можем менять состояние handler
}

// Использование
let mut count = 0;
let mut increase_count = || {
    count += 1;
};

example(increase_count); // Здесь `increase_count` может изменять `count`
```

В этом примере, передавая `increase_count` в `example`, переменная `handler` в `example` не помечена как `mut`, но внутри вызывается она как `FnMut`, что позволяет изменять состояние `count`.
Мутабельность нужна для вызова, так как FnMut вызывается по мутабельной ссылке. <br/> Здесь же только передаётся владение в метод run <br/> <br/> P.S. что-то мне подсказывает, что сигнатура не совсем корректная. Отсутствует объявление дженерика T (и возможно его ограничений), отсутствует лайфтайм для ссылки в колбэке: <pre><code class="rust">pub fn run&lt;T, F&gt;(self, event_handler: F) -&gt; Result&lt;(), EventLoopError&gt;
    where
        F: for&lt;'a&gt; FnMut(Event&lt;T&gt;, &amp;'a EventLoopWindowTarget&lt;T&gt;),
{
    self.event_loop.run(event_handler)
}</code></pre>
Потому что замыкание, которое не имеет мутабельных ссылок на окружение - автоматически реализует трейты Fn, FnMut и FnOnce. <br/> <br/> В функции run указан самый широкий вариант из возможных (Fn обычный был бы слишком узким, а FnOnce для run не подходит, тк замыкание предполагается вызывать много раз) <br/> <br/> <a href="https://habr.com/ru/articles/588917/" rel="nofollow">https://habr.com/ru/articles/588917/</a> <br/> <a href="https://stackoverflow.com/questions/30177395/when-does-a-closure-implement-fn-fnmut-and-fnonce" rel="nofollow">https://stackoverflow.com/questions/30177395/when-...</a>
Похожие вопросы