将Struct <'a> :::方法作为`for <'a> Fn(&Foo <'a>)`传递给闭包使用

时间:2019-05-12 01:56:14

标签: rust closures borrow-checker

在测试中,我有一个辅助函数,该函数可以在不同配置的对象上运行给定方法,其简化版本如下所示:

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);
// }

playground

有关相同错误代码的其他问题的概述:

1 个答案:

答案 0 :(得分:1)

  

我可以通过避免闭包来解决此问题(尽管我不太了解'如何将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的类型中),因此您必须构造一个 that 生存期的Foo才能调用给定run参考。

  

是否可以解决此问题而无需重写?

那要看。

如果所有测试值都是文字,则可以引用'static

如果您能够并且愿意重写run,则可以不受Foo类型的约束

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

但是它根本不需要进入impl块。

我认为注释中提供的解决方案是最好的选择,因为考虑到您并不关心具体的Foo<'a>类型,则无需提供该类型的方法参考。