Вызов self.fn() внутри замыкания

44 views
Skip to first unread message

gig kaz

unread,
Dec 23, 2014, 4:51:35 PM12/23/14
to rust-r...@googlegroups.com
Добрый день. Никак не могу до конца осмыслить замыкания в rust.
Есть структура (MyStruct), имплементация которой содержит функцию fn start(&self). В этой функции необходимо в цикле получать данные (data) и вызвать их обработку в новом потоке, причем функция-обработчик должна быть реализована в структуре MyStruct, так как handle обращается (только читает) к данным структуры.

Возможно ли реализовать такое замыкание?

В коде возникает ошибка, так как я не могу передать self в замыкание:
impl MyStruct {
    pub fn
new<A: ip::ToSocketAddr>(address: A) -> Proxy {
,,,
   
}

    pub fn start
(&self) {
       
for data in ... {
           let data
= ...;
            spawn
(move || {
                let mut data
= data;
                println
!("handle -> {}", self.handle(data));
           
});
       
}
   
}

    fn handle
(&self) {
...
   
}
}


Vladimir Matveev

unread,
Dec 24, 2014, 1:34:20 AM12/24/14
to gig kaz, rust-r...@googlegroups.com
Добрый день,

Нет, так сделать нельзя, и вот почему. Здесь возможны два варианта -
либо вы перемещаете self в новый поток, либо вы вызываете метод на
self из нового потока по ссылке. В первом варианте вы не сможете
создавать потоки в цикле - очевидно, self может быть перемещён только
в один из них (и к тому же это нельзя сделать в принципе, если метод
принимает &self, поскольку из-под ссылки перемещать данные нельзя). Во
втором варианте возникает проблема такого рода:

let v = vec![1, 2, 3];

{
let v_ref = &v;
spawn(move || {
timer::sleep(Duration::seconds(3));
println!("{}", v_ref[0]);
});
}

drop(v);

Ссылка передаётся в поток, а объект, на который она указывает,
дропается в другом потоке (выходит из области видимости и
деструктурируется). Получается висящая ссылка. Из-за этого передавать
нестатические ссылки между потоками небезопасно.

Самый простой способ решить вашу проблему - клонировать self перед
отправкой в поток:

for data in ... {
let data = ...;
let self_cloned = self.clone();
spawn(move || {
println!("handle -> {}", self_cloned.handle(data));
});
}

Безусловно, в этом случае MyStruct должна реализовывать Clone.

Если это для вас не подходит, то вам нужно будет использовать Arc<T>.
В идеале вы могли бы объявить, что методы принимают self через Arc<T>,
но я не уверен, что это сейчас сработает:

fn start(self: Arc<MyStruct>) { ... }

fn handle(self: Arc<MyStruct>) { ... }

Так что вам придётся сделать свободные функции вместо методов, которые
будут использовать Arc<MyStruct> (или Arc<Mutex<MyStruct>>, если к
структуре нужен мутабельный доступ):

fn start(my_struct: Arc<MyStruct>) {
for data in ... {
let data = ...;
let my_struct = my_struct.clone();
spawn(move || {
println!("handle -> {}", handle(my_struct, data));
});
}
}

Кажется, что handle() всё же можно сделать методом, который принимает
&self, а не Arc<MyStruct>, но это уже детали; start() в любом случае
должен принимать Arc<MyStruct>.

24 декабря 2014 г., 0:51 пользователь gig kaz <cht...@gmail.com> написал:
> --
> Вы получили это сообщение, поскольку подписаны на группу "Rust по-русски".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения,
> отправьте письмо на электронный адрес
> rust-russian...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке
> https://groups.google.com/d/msgid/rust-russian/1372c4fc-5dcb-4095-a5a8-c746103aa6d3%40googlegroups.com.
> Чтобы настроить другие параметры, перейдите по ссылке
> https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages