Передайте метод Struct <'a> :: как `for <' a> Fn (& Foo <'a>)` для использования в замыкании - PullRequest
0 голосов
/ 12 мая 2019

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

fn run_method<F>(f: F)
where
    F: Fn(&Foo),
{
    let to_test = vec![0i32];
    to_test
        .iter()
        .map(|param| {
            let foo = Foo(*param);
            f(&foo);
        })
        .for_each(drop);
}

// run_method(Foo::run);

Это работало нормально, пока я не добавил ссылки на протестированную структуру, сделав ее "аннотированной на всю жизнь" (из-за отсутствия правильного термина я имею в виду Foo<'a>).

Теперь я получаю ошибку, указывающую, я думаю, что Rust не хочет принимать Foo::method в качестве функции, которую можно использовать с любым заданным временем жизни (то есть F: for<'a> Fn(&Foo<'a>)), как того требует замыкание:

error[E0631]: type mismatch in function arguments
--> src/main.rs:54:5
   |
3  |     fn run(&self) {
   |     ------------- found signature of `for<'r> fn(&'r Foo<'_>) -> _`
...
54 |     run_method(Foo::run);
   |     ^^^^^^^^^^ expected signature of `for<'r, 's> fn(&'r Foo<'s>) -> _`
   |
note: required by `run_method`
--> src/main.rs:44:1
   |
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0271]: type mismatch resolving `for<'r, 's> <for<'t0> fn(&'t0 Foo<'_>) {Foo::<'_>::run} as std::ops::FnOnce<(&'r Foo<'s>,)>>::Output == ()`
--> src/main.rs:54:5
   |
54 |     run_method(Foo::run);
   |     ^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
|
note: required by `run_method`
--> src/main.rs:44:1
   |
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Я могу обойти эту проблему, избегая замыканий (хотя я не совсем понимаю, как 'a становится локальным по отношению к run_method - разве параметр времени жизни не должен быть выбран вызывающей стороной?):

fn run_method<'a, F>(f: F)
where
    F: Fn(&Foo<'a>),
{
    let to_test = vec![&0i32];
    for param in to_test {
        let foo = Foo(param);
        f(&foo);
    }
}

Можно ли это исправить без переписывания? Если нет - есть ли причина, по которой это не должно работать?

Полный код:

struct Foo<'a>(&'a i32);
impl<'a> Foo<'a> {
    fn run(&self) {
        println!("Hello {}", self.0);
    }
}

fn run_method<F>(f: F)
where
    F: Fn(&Foo),
{
    //same as  F: for<'a> Fn(&Foo<'a>) {
    let to_test = vec![0i32];
    to_test
        .iter()
        .map(|param| {
            let foo = Foo(param);
            f(&foo);
        })
        .for_each(drop);
}

fn main() {
    run_method(Foo::run);
}

// This works:
// fn run_method<'a, F>(f: F)
// where
//     F: Fn(&Foo<'a>),
// {
//     let to_test = vec![&0i32];
//     for param in to_test {
//         let foo = Foo(param);
//         f(&foo);
//     }
// }

// The lifetime-less version:
// struct Foo(i32);
// impl Foo {
//     fn run(&self) {
//         println!("Hello {}", self.0);
//     }
// }
// 
// fn run_parser_method<F>(f: F)
// where
//     F: Fn(&Foo),
// {
//     let to_test = vec![0i32];
//     to_test
//         .iter()
//         .map(|param| {
//             let foo = Foo(*param);
//             f(&foo);
//         })
//         .for_each(drop);
// }
// 
// fn main() {
//     run_parser_method(Foo::run);
// }

детская площадка

Обзор других вопросов по тому же коду ошибки:

1 Ответ

1 голос
/ 14 мая 2019

Я могу обойти эту проблему, избегая замыканий (хотя я не очень понимаю, как a становится локальным по отношению к run_method - разве параметр времени жизни не должен выбираться вызывающей стороной?)

Это так. Но когда вы переписываете его без замыканий, вы также помещаете ссылку в вызов vec!, поэтому он больше не создается во время выполнения. Вместо этого компилятор может сделать вывод, что to_test имеет тип Vec<&'static i32>, и, поскольку 'static переживает любое время, выбранное вызывающим абонентом, нарушения не будет.

Это не скомпилируется, как вы ожидаете:

let to_test = vec![0i32];
for param in to_test.iter() {
    let foo = Foo(param);
    f(&foo);
}

есть ли причина, по которой это не должно работать?

Да, поскольку тип run ограничен типом Foo. Более конкретно, у вас есть время жизни, определенное вызывающей стороной (неявно, в типе для Foo), поэтому вы должны сконструировать Foo из , что продолжительности жизни, чтобы вызвать указанную ссылку run на это.

Можно ли это исправить без переписывания?

Это зависит.

Если все ваши тестовые значения являются литералами, вы можете сделать 'static ссылки.

Если вы можете и хотите переписать run, можно сделать его неограниченным по типу Foo

impl<'a> Foo<'a> {
    fn run<'s>(_self: &Foo<'s>) {
        println!("Hello {}", _self.0);
    }
}

Но тогда ему вообще не нужно входить в блок impl.

Я думаю, что решение, предоставленное в комментариях, является лучшим выбором, поскольку, учитывая, что вам не нужен конкретный тип Foo<'a>, нет необходимости указывать ссылку на метод для этого типа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...