关于这个主题已经有很多线程但是我没有看到讨论的问题是否适用于我的具体问题.
我有一个存储a name和callback函数的结构.剥离问题看起来像这样:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想要做的是存储一个与名称相关的回调函数(以及将来的更多东西).
但是当我尝试使用这样的代码时:
fn main() {
let play_callback = |args| {
println!("Playing something.");
for arg in args {
println!("{}", arg);
}
};
let play_command = Command::new("play", &play_callback);
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误消息:
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);
^~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
我试着像这样内联封闭
fn main() {
let play_command = Command::new("play", &|args| {
println!("Playing something.");
for arg in args {
println!("{}", arg);
}
});
}
Run Code Online (Sandbox Code Playgroud)
但后来又出现了另一个错误
src/main.rs:16:47: 21:7 error: borrowed value does not live long enough
Run Code Online (Sandbox Code Playgroud)
我相信我理解为什么会这样.
我尝试使用泛型类型参数,Command然后在切换到函数引用之前先存储在我的Command结构中,但是当我想初始化一个这样HashSet的命令对象时:
let mut commands: HashSet<Command> = HashSet::new();
Run Code Online (Sandbox Code Playgroud)
编译器希望我指定我认为不能做的泛型参数,因为这意味着我只能在所有Command对象中存储相同的闭包.
所以我的问题是:我怎样才能实现我想要的,最好的方法是什么(以及为什么)?
简单的修复(由ljedrz)是args: &[&str]在这种情况下不推断.但是,它可能不足以解决您的问题.
当您使用对某个特征的引用作为函数参数时,它将被视为特征对象.在这种情况下,&Fn是一个引用堆栈上的闭包的特征对象.
特征对象的简单类比是在其他语言中实现接口的对象.
但是,生命周期与特征对象的工作方式略有不同.您可以将它们视为与通常的所有权流程"分离".如果我们要在示例中注释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);
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,生命周期'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));
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,闭包将在创建框时移动到框中,并将与其一起销毁Command.
| 归档时间: |
|
| 查看次数: |
660 次 |
| 最近记录: |