【问题标题】:How do I create & use a list of callback functions?如何创建和使用回调函数列表?
【发布时间】:2015-03-10 04:29:37
【问题描述】:

在 Rust 中,我正在尝试创建一个回调函数列表以供稍后调用:

use std::vec::Vec;

fn add_to_vec<T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: T) {
    v.push(Box::new(f));
}

fn call_b() {
    println!("Call b.");
}

#[test]
fn it_works() {
    let calls: Vec<Box<FnMut() -> ()>> = Vec::new();

    add_to_vec(&calls, || { println!("Call a."); });
    add_to_vec(&calls, call_b);

    for c in calls.drain() {
        c();
    }
}

我主要遵循here on how to store a closure 的建议,但是,我仍然看到一些错误:

src/lib.rs:6:12: 6:23 error: the parameter type `T` may not live long enough [E0311]
src/lib.rs:6     v.push(Box::new(f));
                        ^~~~~~~~~~~
src/lib.rs:6:23: 6:23 help: consider adding an explicit lifetime bound for `T`
src/lib.rs:5:68: 7:2 note: the parameter type `T` must be valid for the anonymous lifetime #1 defined on the block at 5:67...
src/lib.rs:5 fn add_to_vec<T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: T) {
src/lib.rs:6     v.push(Box::new(f));
src/lib.rs:7 }
src/lib.rs:6:12: 6:23 note: ...so that the type `T` will meet its required lifetime bounds
src/lib.rs:6     v.push(Box::new(f));
                        ^~~~~~~~~~~

我尝试将函数签名更改为:

fn add_to_vec<'a, T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: &'a T) {

……但这让我明白了:

src/lib.rs:6:12: 6:23 error: the trait `core::ops::Fn<()>` is not implemented for the type `&T` [E0277]
src/lib.rs:6     v.push(Box::new(f));
                        ^~~~~~~~~~~
error: aborting due to previous error
src/lib.rs:6:12: 6:23 error: the trait `core::ops::Fn<()>` is not implemented for the type `&T` [E0277]
src/lib.rs:6     v.push(Box::new(f));
                        ^~~~~~~~~~~
src/lib.rs:18:24: 18:51 error: mismatched types:
 expected `&_`,
    found `[closure src/lib.rs:18:24: 18:51]`
(expected &-ptr,
    found closure) [E0308]
src/lib.rs:18     add_to_vec(&calls, || { println!("Call a."); });
                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~

(我可以通过添加&amp; 来纠正的最后一个错误;虽然我认为这是我应该需要的,因为add_to_vec 最终将拥有闭包,因此需要借用它,我不完全确定。)

【问题讨论】:

    标签: closures rust


    【解决方案1】:

    您的代码存在一些问题。这是一个完全修复的版本:

    use std::vec::Vec;
    
    fn add_to_vec<'a, T: FnMut() + 'a>(v: &mut Vec<Box<FnMut() + 'a>>, f: T) {
        v.push(Box::new(f));
    }
    
    fn call_b() {
        println!("Call b.");
    }
    
    #[test]
    fn it_works() {
        let mut calls: Vec<Box<FnMut()>> = Vec::new();
    
        add_to_vec(&mut calls, || { println!("Call a."); });
        add_to_vec(&mut calls, call_b);
    
        for mut c in calls.drain() {
            c();
        }
    }
    

    生命周期问题是装箱的函数对象必须有一个共同的基本生命周期;如果您只编写通用约束T: FnMut(),则假定它只需要与函数调用一样长,而不再需要。因此,需要添加两件事:泛型参数T 必须被约束到指定的生命周期,并且为了将其存储在向量中,特征对象类型必须同样受到约束,如Box&lt;FnMut() + 'a&gt;。这样它们都匹配并确保内存安全,因此编译器允许它通过。顺便说一下,FnMut() -&gt; ()-&gt; () 部分是多余的。

    剩下的需要做的修复是插入几个mut;为了推送到向量,您自然需要一个可变引用,因此&amp;&amp;mut 的变化,并且为了获取对callsc 的可变引用,必须进行绑定mut

    【讨论】:

    • 为什么,在这段代码中,如果我将Box::new(f) 更改为Box::&lt;FnMut() + 'a&gt;::new(f),它会编译失败吗?我不只是完全指定类型参数而不是让 Rust 推断它们吗?
    • 另外,在您的示例中,在add_to_vec 中,为什么我们必须完全指定Box&lt;FnMut() + 'a&gt;,为什么这不等于T
    • @Thanatos:你的意思是在向量中,它是&amp;mut Vec&lt;Box&lt;FnMut() + 'a&gt;&gt;而不是Vec&lt;T&gt;?想一想,应该会明白:向量的类型应该是什么?
    • 是的,向量,但不是Vec&lt;Box&lt;T&gt;&gt;;因为TFnMut() + 'aVec&lt;T&gt; 将是Vec&lt;FnMut() + 'a&gt;,它缺少我们的Box,这显然是不同的;我在问&amp;mut Vec&lt;Box&lt;FnMut() + 'a&gt;&gt;&amp;mut Vec&lt;Box&lt;T&gt;&gt; 有何不同?
    • T不是 FnMut() + 'a; T 是一个具体类型,它实现 FnMut() 并至少存在'a。请记住,在处理函数和闭包时,每个都是唯一的类型。仅以call_b 为例,它有一个以友好形式(用于编译器错误)的类型,写成fn() {call_b}Tfn() {call_b},所以Vec&lt;Box&lt;T&gt;&gt; 将是Vec&lt;Box&lt;fn() {call_b}&gt;&gt;
    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 2018-09-21
    • 2017-02-06
    • 2013-09-15
    • 1970-01-01
    相关资源
    最近更新 更多