代码之家  ›  专栏  ›  技术社区  ›  ash

如何从带有“静态绑定”的闭包中构造容器?[复制品]

  •  0
  • ash  · 技术社区  · 7 年前

    这个问题已经有了答案:

    我正在使用 libpulse_binding 图书馆,我正试图获得 SinkInputInfo 来自S get_sink_input_info_list 功能:

    pub fn get_sink_input_info_list<F>(
        &self,
        callback: F,
    ) -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
    where
        F: FnMut(ListResult<&SinkInputInfo>) + 'static,
    

    函数接受回调并为每个回调调用一次 辛金普夫 它产生。我正在收集所有这些 辛金普夫 列成一个单子,这样我就能更清楚地了解世界的状况。恼人地, 辛金普夫 不执行 Copy Clone ,因此我创建了一个自定义结构并实现 From 把有用的信息从 辛金普夫 :

    struct StreamInfo {
        readable_name: String,
        binary_name: String,
        pid: String,
    }
    
    impl From<&pulse::context::introspect::SinkInputInfo<'_>> for StreamInfo {
        fn from(info: &pulse::context::introspect::SinkInputInfo) -> Self {
            let name = info.proplist.gets("application.name").unwrap();
            let binary = info.proplist.gets("application.process.binary").unwrap();
            let pid = info.proplist.gets("application.process.id").unwrap();
            StreamInfo {
                readable_name: name,
                binary_name: binary,
                pid: pid,
            }
        }
    }
    

    然而,这似乎不起作用。我有以下代码:

    let mut sink_infos: Vec<StreamInfo> = Vec::new();
    let op = introspector.get_sink_input_info_list(|result| match result {
        pulse::callbacks::ListResult::Item(info) => sink_infos.push(info.into()),
        pulse::callbacks::ListResult::End => {},
        pulse::callbacks::ListResult::Error => panic!("Error getting sink input info"),
    });
    

    但它不能编译:

    error[E0373]: closure may outlive the current function, but it borrows `sink_infos`, which is owned by the current function
      --> src/bin/play-pause.rs:49:52
       |
    49 |     let op = introspector.get_sink_input_info_list(|result| match result {
       |                                                    ^^^^^^^^ may outlive borrowed value `sink_infos`
    50 |         pulse::callbacks::ListResult::Item(info) => sink_infos.push(info.into()),
       |                                                     ---------- `sink_infos` is borrowed here
       |
    note: function requires argument type to outlive `'static`
      --> src/bin/play-pause.rs:49:14
       |
    49 |       let op = introspector.get_sink_input_info_list(|result| match result {
       |  ______________^
    50 | |         pulse::callbacks::ListResult::Item(info) => sink_infos.push(info.into()),
    51 | |         pulse::callbacks::ListResult::End => {},
    52 | |         pulse::callbacks::ListResult::Error => panic!("Error getting sink input info"),
    53 | |     });
       | |______^
    help: to force the closure to take ownership of `sink_infos` (and any other referenced variables), use the `move` keyword
       |
    49 |     let op = introspector.get_sink_input_info_list(move |result| match result {
       |                                                    ^^^^^^^^^^^^^
    

    关闭必须有 'static 因为libpulse-bunding是这么说的(大概是因为它被交给了pulseaudio C API,然后可以用它做任何它喜欢的事情),但是 sink_infos 不是 静态 关闭时必须借用 新西兰 为了附加到它,这使得闭包 静态 ,IIUC。

    我怎么做 Vec (或任何容器,我不挑剔)的 辛金普夫 给出了 静态 用一个 &SinkInputInfo ?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Shepmaster Tim Diekmann    7 年前

    这里的基本问题是你遇到了 Rust's borrowing rules :

    给定对象 T ,只能有以下之一:

    • 有几个不变的引用( &T )到对象(也称为 混叠 )
    • 有一个可变的参照物( &mut T )到对象(也称为 易变性 )

    你想留个证明人 &Vec 到您的vec(以便您以后可以使用它),同时尝试在一个闭包中添加东西(即 &mut Vec )铁锈不知道你不会用 &VEC 当关闭正在使用 &穆特VEC ,所以它不允许您创建 &穆特VEC 在结束的时候 &VEC 在封闭的外面闲逛。

    你能做的第二件事就是使用 Rc . 这将允许您回避编译器的借用检查,而将其推迟到运行时。但是:这意味着如果您在程序运行时试图违反借用规则,那么它将恐慌而不是编译时错误!

    在大多数情况下,你可以 Rc<Vec<_>> 和正常人一样 Vec ,因为 钢筋混凝土 器具 Deref .

    因为你也希望能够变异 VEC 为了添加内容,您还需要将其放入 RefCell . 这将在 VEC ,确保您只有一个 &穆特VEC 立即提供,如果您有 &VEC 你不能 &穆特VEC (同样,如果你试图违反规则,你的程序会恐慌)。你可以使用 .borrow() .borrow_mut() 方法对 重电池 获取对vec的共享和可变引用(还有 try_* 这些方法的变体,如果您可以做一些明智的事情,如果借用是不可能的)。

    如果你不使用 重电池 ,您将只能获取不可变/共享的引用( &VEC )来自 钢筋混凝土 (除非你只有一个 钢筋混凝土 但是你不需要 钢筋混凝土 !)

    尝试如下操作:

    use std::cell::RefCell;
    use std::rc::Rc;
    
    let sink_infos: Rc<RefCell<Vec<StreamInfo>>> = Rc::new(RefCell::new(Vec::new()));
    let sink_infos2 = sink_infos.clone(); // Create a new Rc which points to the same data.
    let op = introspector.get_sink_input_info_list(move |result| match result {
        pulse::callbacks::ListResult::Item(info) => sink_infos2.borrow_mut().push(info.into()),
        pulse::callbacks::ListResult::End => {},
        pulse::callbacks::ListResult::Error => panic!("Error getting sink input info"),
    });
    
    推荐文章