【问题标题】:Function references: expected bound lifetime parameter , found concrete lifetime [E0271]函数引用:预期绑定生命周期参数,找到具体生命周期 [E0271]
【发布时间】:2016-09-08 16:14:08
【问题描述】:

关于这个主题已经有很多线程,但我看不到讨论的问题是否适用于我的具体问题。

我有一个存储namecallback 函数的结构。剥离到它看起来像这样的问题:

pub struct Command<'a> {
    name: &'a str,
    callback: &'a Fn(&[&str]) -> ()
}

impl <'a> Command<'a> {
    pub fn new(name: &'a str, callback: &'a Fn(&[&str]) -> ()) -> Command<'a> {
        Command {
            name: name,
            callback: callback
        }
    }
}

我想要做的是存储一个与名称相关联的回调函数(以及未来可能更多的东西)。

但是当我尝试像这样使用这段代码时:

fn main() {
    let play_callback = |args| {
        println!("Playing something.");
        for arg in args {
            println!("{}", arg);
        }
    };
    let play_command = Command::new("play", &play_callback);
}

我收到以下错误消息:

src/main.rs:22:42: 22:56 error: type mismatch resolving `for<'r, 'r> <[closure@src/main.rs:16:22: 21:3] as std::ops::FnOnce<(&'r [&'r str],)>>::Output == ()`:
 expected bound lifetime parameter ,
    found concrete lifetime [E0271]
src/main.rs:22  let play_command = Command::new("play", &play_callback);
                                                        ^~~~~~~~~~~~~~

我试图像这样内联闭包

fn main() {
    let play_command = Command::new("play", &|args| {
        println!("Playing something.");
        for arg in args {
            println!("{}", arg);
        }
    });
}

然后我又遇到一个错误

src/main.rs:16:47: 21:7 error: borrowed value does not live long enough

我相信我明白为什么会这样。

在切换到函数引用以首先存储在我的Command 结构中之前,我尝试为Command 使用泛型类型参数,但是当我想初始化这样的命令对象的HashSet 时:

let mut commands: HashSet<Command> = HashSet::new();

编译器希望我指定我认为我无法做到的通用参数,因为这意味着我只能在我的所有 Command 对象中存储相同的闭包。

所以我的问题是:我怎样才能实现我想要的,最好的方法是什么(以及为什么)?

【问题讨论】:

    标签: rust lifetime


    【解决方案1】:

    简单的解决方法(由 ljedrz 提供)是在这种情况下不会推断出 args: &amp;[&amp;str]。但是,这可能还不足以解决您的问题。

    当您将某个 trait 的引用用作函数参数时,它会被视为 trait 对象。在这种情况下,&amp;Fn 是一个 trait 对象,它引用堆栈上的闭包。

    特征对象的一个​​简单类比是实现其他语言接口的对象。

    但是,生命周期与 trait 对象的工作方式略有不同。您可以将它们视为与通常的所有权流程“分离”。如果我们要在您的示例中注释 Fn 特征对象生命周期 'c,我们将得到以下代码:

    pub struct Command<'a> {
        name: &'a str,
        callback: &'a for<'c> Fn(&'c [&'c str]) -> ()
    }
    
    impl <'a> Command<'a> {
        pub fn new<'r>(name: &'r str, callback: &'r for<'c> Fn(&'c [&'c str]) -> ()) -> Command<'r> {
            Command {
                name: name,
                callback: callback
            }
        }
    }
    
    fn main() {
        let play_callback = |args: &[&str]| {
            println!("Playing something.");
            for arg in args {
                println!("{}", arg);
            }
        };
        let play_command = Command::new("play", &play_callback);
    }
    

    在上面的代码中,生命周期'c 描述了回调函数将被调用的范围。

    然而,上面的代码不是很实用。它将命令与创建闭包的范围耦合(请记住,特征对象引用该范围内的闭包)。所以你不能退出创建 Command 的函数,因为那会破坏闭包!

    可能的解决方案是将闭包存储在堆上,在Box&lt;Fn(&amp;[&amp;str])&gt; 中。 box(堆内存)中 trait 对象的生命周期由 box 的创建和销毁控制,因此它是最广泛的('static)。

    pub struct Command<'a> {
        name: &'a str,
        callback: Box<Fn(&[&str]) -> ()>
    }
    
    impl <'a> Command<'a> {
        pub fn new<'r>(name: &'r str, callback: Box<Fn(&[&str]) -> ()>) -> Command<'r> {
            Command {
                name: name,
                callback: callback
            }
        }
    }
    
    fn main() {
        let play_callback = |args: &[&str]| {
            println!("Playing something.");
            for arg in args {
                println!("{}", arg);
            }
        };
        let play_command = Command::new("play", Box::new(play_callback));
    }
    

    在上面的例子中,闭包会在盒子被创建的时候移动到盒子里,和Command一起被销毁。

    【讨论】:

      【解决方案2】:

      您是否尝试过指定args 的类型?以下为我编译:

      fn main() {
          let play_callback = |args: &[&str]| {
              println!("Playing something.");
              for arg in args {
                  println!("{}", arg);
              }
          };
          let play_command = Command::new("play", &play_callback);
      }
      

      不过,我不知道为什么它没有被推断出来。

      【讨论】:

        猜你喜欢
        • 2020-11-21
        • 1970-01-01
        • 2014-09-10
        • 2015-09-30
        • 1970-01-01
        • 1970-01-01
        • 2015-12-08
        • 1970-01-01
        • 2015-04-22
        相关资源
        最近更新 更多