首页 文章

函数引用:预期的约束生命周期参数,找到具体的生命周期[E0271]

提问于
浏览
3

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

我有一个存储 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 对象中存储相同的闭包 .

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

2 回答

  • 5

    简单的修复(由ljedrz)是在这种情况下不推断 args: &[&str] . 但是,它可能不足以解决您的问题 .

    当您使用对某个特征的引用作为函数参数时,它将被视为特征对象 . 在这种情况下, &Fn 是一个特征对象,它引用堆栈上的闭包 .

    特征对象的简单类比是在其他语言中实现接口的对象 .

    但是,生命周期与特征对象的工作方式略有不同 . 您可以将它们视为通常所有权流中的"detached" . 如果我们要在您的示例中注释 Fn trait对象的生命周期 '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 描述了将调用回调函数的范围 .

    但是,上述代码不太实用 . 它将命令耦合到创建闭包的范围(请记住,trait对象引用该范围内的闭包) . 因此,您无法退出创建 Command 的函数,因为这会破坏闭包!

    可能的解决方案是在 Box<Fn(&[&str])> 中将闭包存储在堆上 . 框中的特征对象(堆内存)的生命周期由框的创建和销毁控制,因此它是最广泛的( '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);
    }
    

    不过,我不知道为什么不推断 .

相关问题