如何在带有“静态绑定”的闭包内构造容器?

时间:2018-12-27 21:17:38

标签: rust closures lifetime

我正在使用libpulse_binding库,并且尝试从SinkInputInfo函数中获取get_sink_input_info_list的序列:

public class AttributeRule implements Template {

    @Override
    public String writeTemplate(BusinessRule businessRule) throws Exception {
        String link = TemplateReader.getInstance().getLinkToQuery(businessRule.getBusinessRuleTypeCode());
        String template = TemplateReader.getInstance().readQuery(link);
        ST templateFixer = new ST(template);
        templateFixer.add("code", businessRule.getBusinessRuleTypeCode());
        templateFixer.add("attribute_table", businessRule.getListOfTables().get(0).getName());
    }
}

public class AttributeCompareRule extends AttributeRule {

    @Override
    public String writeTemplate(BusinessRule businessRule) throws Exception {
        super.writeTemplate(rule);
        // Custom class code here
        templateFixer.add("operand", businessRule.getOperator().getName());
        templateFixer.add("compare_with", businessRule.getListOfValues().get(0).getValue());
        templateFixer.add("error", businessRule.getErrorMessage());
        String templateDLL = templateFixer.render();
        return templateDLL;
    }
}

public class AttributeRangeRule extends AttributeRule {
     super.writeTemplate(rule);
    // Custom class code here
}

该函数接受一个回调,并对其产生的每个pub fn get_sink_input_info_list<F>( &self, callback: F, ) -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)> where F: FnMut(ListResult<&SinkInputInfo>) + 'static, 调用一次。我正在尝试将所有SinkInputInfo收集到一个列表中,以便可以更清楚地了解世界状况。令人烦恼的是,SinkInputInfo没有实现SinkInputInfoCopy,因此我制作了一个自定义结构并实现了Clone以从From中获取有用的信息:

SinkInputInfo

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

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"),
});

tldr:闭包必须具有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 { | ^^^^^^^^^^^^^ 的生存时间,因为libpulse_binding表示(大概是因为它被交给了PulseAudio C API,然后可以用它做任何事情),但是'static不是sink_infos,并且闭包必须借用'static才能附加到它,这使得闭包不是sink_infos,IIUC。

在给定'static闭包并用Vec反复调用的情况下,如何制作SinkInputInfo的{​​{1}}(或其他容器,我并不挑剔) ?

1 个答案:

答案 0 :(得分:1)

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

  

鉴于对象T,只能具有以下之一:

     
      
  • 对该对象具有几个不变的引用(&T)(也称为 aliasing )。
  •   
  • 对该对象具有一个可变的引用(&mut T)(也称为可变性)。
  •   

您正在尝试保留对Vec的引用&Vec(以便以后可以使用),同时尝试在闭包中添加内容(即&mut Vec)。 Rust不知道在闭包使用&Vec时您不会尝试使用&mut Vec,因此在闭包中使用&mut Vec时您不会创建&Vec仍然有Rc<Vec<_>>挂在封包外部。

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

在大多数情况下,由于Vec实现了Rc,因此您可以将Deref与普通Vec相同。

由于您还希望能够突变Vec以便向其中添加内容,因此您还需要将其放入RefCell中。这将锁定&mut Vec,确保同时只有一个&Vec可用,并且如果您有&mut Vec,则不能拥有{{1 }}(同样,如果您尝试违反规则,您的程序将崩溃)。您可以在.borrow()上使用.borrow_mut()RefCell方法来获取对Vec的共享和可变引用(如果可以的话,这些方法也有try_*个变体如果无法借钱,那就明智了。

如果您不使用RefCell,则只能从&Vec获取不可变/共享的引用(Rc)(除非您只有一个{{1 }},但您不需要Rc!)

尝试以下操作:

Rc