Как объяснено в ответе Шепмастера, вы можете перемещать значение из переменной только один раз, и компилятор не позволит вам сделать это во второй раз.Я постараюсь добавить немного конкретного контекста для этого варианта использования.Большая часть этого написана на моей памяти об использовании GTK из C давным-давно, и несколько раз я только что посмотрел в документации gtk-rs, так что я уверен, что некоторые детали были неверными, но я думаю, что общая суть точна.
Давайте сначала посмотрим, почему вам нужно сначала переместить значение в замыкания.Методы, которые вы вызываете для list_box
внутри обоих замыканий, принимают self
по ссылке, поэтому вы фактически не используете список в замыканиях.Это означает, что было бы совершенно правильно определить два замыкания без спецификаторов move
- вам нужны только ссылки только для чтения на list_box
, вам разрешено иметь более одной ссылки только для чтения одновременно и list_box
живет по крайней мере столько же, сколько замыкания.
Однако, хотя вам разрешено определять два замыкания, не перемещая list_box
в них, вы не можете передать замыкания, определенные таким образомк gtk-rs: все функции, соединяющие только обработчики событий, кроме «статических» функций, например
fn connect_search_changed<F: Fn(&Self) + 'static>(
&self,
f: F
) -> SignalHandlerId
Тип обработчика F
имеет границу черты Fn(&Self) + 'static
, что означает, что замыкание можетне содержит никаких ссылок, или все ссылки, которые он содержит, должны иметь статическое время жизни.Если мы не переместим list_box
в замыкание, замыкание будет содержать нестатическую ссылку на него.Поэтому нам нужно избавиться от ссылки, прежде чем мы сможем использовать функцию в качестве обработчика событий.
Почему gtk-rs накладывает это ограничение?Причина в том, что gtk-rs является оболочкой для набора библиотек C, и указатель на обратный вызов в конечном итоге передается базовой библиотеке glib
.Поскольку C не имеет понятия времени жизни, единственный способ сделать это безопасно - это потребовать, чтобы не было никаких ссылок, которые могут стать недействительными.
Теперь мы установили, что наши замыкания не могут содержать никакихРекомендации.Нам по-прежнему необходимо получить доступ к list_box
из замыканий, так какие у нас варианты?Если у вас есть только одно закрытие, то с помощью move
можно добиться успеха - переместив list_box
в закрытие, закрытие становится его владельцем.Тем не менее, мы видели, что это не работает для более чем одного замыкания, потому что мы можем двигаться только list_box
один раз.Нам нужно найти способ иметь несколько владельцев для него, и стандартная библиотека Rust предоставляет такой способ: указатели подсчета ссылок Rc
и Arc
.Первый используется для значений, доступ к которым возможен только из текущего потока, тогда как последний безопасен для перемещения в другие потоки.
Если я правильно помню, glib выполняет все обработчики событий в основном потоке и признакграницы для закрытия отражают это: закрытие не обязательно должно быть Send
или Sync
, поэтому мы должны иметь возможность обойтись с Rc
.Более того, нам нужен только доступ для чтения к list_box
в замыканиях, поэтому нам не нужны RefCell
или Mutex
для внутренней изменчивости в этом случае.Таким образом, все, что вам нужно, вероятно, это:
use std::rc::Rc;
let list_box: gtk::ListBox = builder.get_object("list_box").unwrap();
let list_box_1 = Rc::new(list_box);
let list_box_2 = list_box_1.clone();
Теперь у вас есть два «собственных» указателя на один и тот же список, и эти указатели можно переместить в два замыкания.
Отказ от ответственности: я не мог действительно протестировать ничего из этого, так как ваш пример кода не является автономным.