Функция, возвращающая замыкание, не работает внутри моего фильтра

Я не могу заставить это скомпилировать без использования закрытия. Я пытаюсь заставить функцию apply возвращать правильный тип закрытия в первую очередь.

#![feature(conservative_impl_trait)]
#![allow(dead_code)]

fn accumulate<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) {

    // this works
    let _ = tuples.iter().filter(|t| apply(second, i)(t));

    // this doesn't
    //let f = apply(second, i);
    //let _ = tuples.iter().filter(f);

    //this works as well

    let f  = |t: &&(_,_)| apply(second, i)(t);
    let _ = tuples.iter().filter(f);
}

fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl FnMut(B) -> C
         where F: FnMut(B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}


fn second<A, B: ?Sized>(&(_, ref second): &(A, B)) -> &B {
    second
}

fn main()  {}

Что я могу сделать, чтобы apply работало так, как я хочу?


person iopq    schedule 16.09.2016    source источник
comment
Основываясь на ваших отзывах о моем ответе, я предлагаю вам создать минимально воспроизводимый пример вашей проблемы. Возможно что-то вроде этого.   -  person Shepmaster    schedule 17.09.2016
comment
Я сократил его до apply и second — я пытаюсь решить проблему с HRTB.   -  person iopq    schedule 17.09.2016
comment
Какой язык не поддерживает даже такую ​​базовую операцию, как извлечение подвыражения, присвоение ему имени и уверенность в том, что он делает то же самое?   -  person Miles Rout    schedule 17.03.2018


Ответы (1)


Прежде всего, позвольте мне сказать, что проблема не имеет ничего общего с использованием синтаксиса impl Trait. Я преобразовал закрытие в именованную структуру и получил те же результаты.

Итак, давайте посмотрим на код, который вы хотели бы заставить работать:

let f = apply(second, i);
let _ = tuples.iter().filter(f);

Что компилятор может сказать по этому поводу?

error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

Итак, у нас есть тип X, и ему нужно реализовать черту Y, но этого не происходит. Но давайте посмотрим внимательно:

for<'r> impl
std::ops::FnMut<(&(_, _),)>:
std::ops::FnMut<(&'r &(_, _),)>

Ах ха! filter ожидает функцию, которая принимает ссылку на ссылку на кортеж, тогда как функция, которую мы передаем, принимает ссылку на кортеж. filter передает ссылку на ссылку, потому что tuples.iter() перебирает ссылки, а filter передает ссылки на них.

Хорошо, давайте изменим определение second, чтобы принимать ссылки на ссылки:

fn second<'a, A, B: ?Sized>(&&(_, ref second): &&'a (A, B)) -> &'a B {
    second
}

Компилятор все еще недоволен:

error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

error[E0271]: type mismatch resolving `for<'r> <impl std::ops::FnMut<(&&(_, _),)> as std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>>::Output == bool`
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ expected bound lifetime parameter , found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#24r

expected bound lifetime parameter , found concrete lifetime... Что это значит?

Тип f — это некоторый тип, который реализует FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool. При вызове apply, B == &'c &'b (&'a str, &Fn(i32) -> bool) и C == bool. Обратите внимание, что B здесь является одним фиксированным типом; 'c представляет одно фиксированное время жизни, которое называется конкретным временем жизни.

Давайте посмотрим на подпись filter:

fn filter<P>(self, predicate: P) -> Filter<Self, P> where
    Self: Sized, P: FnMut(&Self::Item) -> bool,

Здесь P должен реализовать FnMut(&Self::Item) -> bool. На самом деле, этот синтаксис является сокращением для for<'r> FnMut(&'r Self::Item) -> bool. Здесь. 'r — это связанный параметр времени жизни.

Итак, проблема в том, что наша функция, реализующая FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool, не реализует for<'r> FnMut(&'r Self::Item) -> bool. Нам понадобится функция, реализующая for<'c> FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool. Единственный способ сделать это на данный момент — написать apply следующим образом:

fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl FnMut(&B) -> C
         where F: FnMut(&B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}

или более явная версия:

fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl for<'r> FnMut(&'r B) -> C
         where F: for<'r> FnMut(&'r B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}

Если Rust в конечном итоге поддерживает более высокий уровень типов, может найти более элегантный способ решить эту проблему.

person Francis Gagné    schedule 17.09.2016