Замыкание в замыкании.

65 views
Skip to first unread message

Mike Potanin

unread,
Nov 28, 2014, 3:13:32 AM11/28/14
to rust-r...@googlegroups.com

Добрый день!
У меня снова вопрос :-)
Почему-то не получается использовать замыкание из замыкания:

#![feature(unboxed_closures)]

type ParseResult<S /*:Seq<C>*/,T> = Vec<(T,S)>;
type Parser<S /*:Seq<C>*/,T> = Box<Fn<(S,), ParseResult<S,T>> + 'static>;

fn pfilter<S>(p:Parser<S,char>, f:Box<Fn<(char,), bool>>) -> Parser<S,char> {
  println!("{}", f.call(('a',)));
  box move |s:S| {
    p.call((s,)).into_iter().filter(|v| match *v { (r,_) => f.call((r,)) }).collect()
  }
}

fn main() {
  println!("{}", pfilter(box move |s:&str| vec![('1',s.clone())], box move |c:char| c == '1').call(("123",)));
}

Ругается
f.rs:8:12: 10:4 error: cannot infer an appropriate lifetime due to conflicting requirements
f.rs:8   box move |s:S| {
f.rs:9     p.call((s,)).into_iter().filter(|v| match *v { (r,_) => f.call((r,)) }).collect()
f.rs:10   }
f.rs:6:77: 11:2 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 6:76...
f.rs:6 fn pfilter<S>(p:Parser<S,char>, f:Box<Fn<(char,), bool>>) -> Parser<S,char> {
f.rs:7   println!("{}", f.call(('a',)));
f.rs:8   box move |s:S| {
f.rs:9     p.call((s,)).into_iter().filter(|v| match *v { (r,_) => f.call((r,)) }).collect()
f.rs:10   }
f.rs:11 }
f.rs:8:12: 10:4 note: ...so that the variable `f` can be captured into a proc
f.rs:8   box move |s:S| {
f.rs:9     p.call((s,)).into_iter().filter(|v| match *v { (r,_) => f.call((r,)) }).collect()
f.rs:10   }
note: but, the lifetime must be valid for the static lifetime...
f.rs:8:3: 10:4 note: ...so that it can be closed over into an object
f.rs:8   box move |s:S| {
f.rs:9     p.call((s,)).into_iter().filter(|v| match *v { (r,_) => f.call((r,)) }).collect()
f.rs:10   }
error: aborting due to previous error

Возможно ли это обойти не отказываясь от filter и unboxed closure?

Vladimir Matveev

unread,
Nov 28, 2014, 10:04:55 AM11/28/14
to Mike Potanin, rust-r...@googlegroups.com
Добрый вечер!

У вас ошибка в сигнатуре функции `pfilter()`:

> fn pfilter<S>(p:Parser<S,char>, f:Box<Fn<(char,), bool>>) -> Parser<S,char> {

Здесь не указан lifetime-параметр у `f`, который должен обязательно быть `’static`, потому что `f` перемещается в окружение замыкания `Parser`:

fn pfilter<S>(p:Parser<S,char>, f:Box<Fn<(char,), bool> + ’static>) -> Parser<S,char> {

После этого программа скомпилируется. Я подозреваю, что компилятор вам этого не говорит из-за lifetime elision, но это весьма подозрительно.

Вообще же здесь передача `f` внутри `Box` не обязательна. Поскольку замыкание передаётся внутрь функции и там же и остаётся, то лучше использовать дженерики. В целом, вот слегка обновлённый вариант:

#![feature(unboxed_closures)]

type ParseResult<S /*:Seq<C>*/,T> = Vec<(T, S)>;
type Parser<S /*:Seq<C>*/,T> = Box<Fn<(S,), ParseResult<S, T>> + 'static>;

fn pfilter<S, F>(p: Parser<S, char>, f: F) -> Parser<S, char>
where F: Fn(char) -> bool, F: 'static {
println!("{}", f('a'));
box move |s: S| {
p.call((s,)).into_iter().filter(|v| match *v { (ref r, _) => f(*r) }).collect()
}
}

fn main() {
println!("{}", pfilter(box move |s: &str| vec![('1', s.clone())], move |c| c == '1').call(("123",)));
}

Компилируется и работает, насколько я могу судить.
> --
> Вы получили это сообщение, поскольку подписаны на группу "Rust по-русски".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес rust-russian...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/rust-russian/eaadf7ac-980f-41df-a50d-9f8a6ea00528%40googlegroups.com.
> Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Mike Potanin

unread,
Dec 1, 2014, 3:06:03 AM12/1/14
to rust-r...@googlegroups.com

 Thanks!
Правда в общем варианте пришлось еще один .clone() добавить.

fn pfilter<T:Clone,S,F>(p:Parser<S,T>, f:F) -> Parser<S,T>
     where F: Fn(T) -> bool, F: 'static {
  box move |s:S| {
    p.call((s,)).into_iter().filter(|v| match *v { (ref r,_) => f(r.clone()) }).collect()

Vladimir Matveev

unread,
Dec 1, 2014, 3:36:13 AM12/1/14
to Mike Potanin, rust-r...@googlegroups.com
А, если вам нужен предикат не для Copy-типов, то его действительно лучше объявлять с аргументом-ссылкой:


where F: Fn(&T) -> bool


В этом случае `T: Clone` не понадобится.
> --
> Вы получили это сообщение, поскольку подписаны на группу "Rust по-русски".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес rust-russian...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/rust-russian/2f95c395-bcf3-4d6f-bd2f-aed5f84b6e48%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages