如何从带有“静态绑定”的闭包中构建容器?
How can I construct a container from within a closure with a 'static bound?
我正在使用 libpulse_binding library, and I'm trying to obtain a sequence of SinkInputInfo
s from the 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,
该函数接受一个回调,并为它产生的每个 SinkInputInfo
调用一次。我正在尝试将所有这些 SinkInputInfo
收集到一个列表中,以便我可以更清楚地了解世界状况。令人恼火的是,SinkInputInfo
没有实现 Copy
或 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"),
});
但无法编译:
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 {
| ^^^^^^^^^^^^^
tldr:闭包必须具有 'static
生命周期,因为 libpulse_binding 是这么说的(大概是因为它被交给了 PulseAudio C API,它可以随心所欲地做任何事情与它),但 sink_infos
不是 'static
,闭包必须借用 sink_infos
才能附加到它,这使得闭包不是 'static
,IIUC.
我怎样才能制作一个Vec
(或任何容器,我不挑剔)给定一个 'static
闭包,这个闭包被 [=31 重复调用]SinkInputInfo
s =]?
这里的基本问题是您 运行 进入 Rust's borrowing rules:
Given an object T
, it is only possible to have one of the following:
- Having several immutable references (
&T
) to the object (also known as aliasing).
- Having one mutable reference (
&mut T
) to the object (also known as mutability).
您正在尝试保留对 Vec 的引用 &Vec
(以便您以后可以使用它),同时尝试在闭包中向其添加内容(即 &mut Vec
)。 Rust 不知道你不会在闭包使用 &mut Vec
时尝试使用 &Vec
,所以它不会让你在闭包中创建一个 &mut Vec
仍然是 &Vec
在闭包外徘徊。
您可以做的下一个最好的事情是使用 Rc
。这将允许您避开编译器的借用检查,而是将其推迟到运行时。然而:这意味着如果你在你的程序 运行 时试图违反借用规则,它会 panic 而不是编译时错误!
在大多数情况下,您可以将 Rc<Vec<_>>
视为普通的 Vec
,因为 Rc
实现了 Deref
.
由于您还希望能够改变 Vec
以便向其添加内容,因此您还需要将其放入 RefCell
中。这将锁定 Vec
,确保您一次只有一个 &mut Vec
可用,如果您有 &Vec
,则不能有 [=15] =](同样,如果你试图违反规则,你的程序将会崩溃)。您可以在 RefCell
上使用 .borrow()
和 .borrow_mut()
方法来获取对 Vec 的共享和可变引用(如果您可以做一些事情,这些方法还有 try_*
变体如果不可能借用是明智的。
如果您不使用 RefCell
,您将只能从 Rc
获得 immutable/shared 引用(&Vec
)(除非您只有一个 Rc
,但那样你就不需要 Rc
!)
尝试如下操作:
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"),
});
我正在使用 libpulse_binding library, and I'm trying to obtain a sequence of SinkInputInfo
s from the 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,
该函数接受一个回调,并为它产生的每个 SinkInputInfo
调用一次。我正在尝试将所有这些 SinkInputInfo
收集到一个列表中,以便我可以更清楚地了解世界状况。令人恼火的是,SinkInputInfo
没有实现 Copy
或 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"),
});
但无法编译:
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 {
| ^^^^^^^^^^^^^
tldr:闭包必须具有 'static
生命周期,因为 libpulse_binding 是这么说的(大概是因为它被交给了 PulseAudio C API,它可以随心所欲地做任何事情与它),但 sink_infos
不是 'static
,闭包必须借用 sink_infos
才能附加到它,这使得闭包不是 'static
,IIUC.
我怎样才能制作一个Vec
(或任何容器,我不挑剔)给定一个 'static
闭包,这个闭包被 [=31 重复调用]SinkInputInfo
s =]?
这里的基本问题是您 运行 进入 Rust's borrowing rules:
Given an object
T
, it is only possible to have one of the following:
- Having several immutable references (
&T
) to the object (also known as aliasing).- Having one mutable reference (
&mut T
) to the object (also known as mutability).
您正在尝试保留对 Vec 的引用 &Vec
(以便您以后可以使用它),同时尝试在闭包中向其添加内容(即 &mut Vec
)。 Rust 不知道你不会在闭包使用 &mut Vec
时尝试使用 &Vec
,所以它不会让你在闭包中创建一个 &mut Vec
仍然是 &Vec
在闭包外徘徊。
您可以做的下一个最好的事情是使用 Rc
。这将允许您避开编译器的借用检查,而是将其推迟到运行时。然而:这意味着如果你在你的程序 运行 时试图违反借用规则,它会 panic 而不是编译时错误!
在大多数情况下,您可以将 Rc<Vec<_>>
视为普通的 Vec
,因为 Rc
实现了 Deref
.
由于您还希望能够改变 Vec
以便向其添加内容,因此您还需要将其放入 RefCell
中。这将锁定 Vec
,确保您一次只有一个 &mut Vec
可用,如果您有 &Vec
,则不能有 [=15] =](同样,如果你试图违反规则,你的程序将会崩溃)。您可以在 RefCell
上使用 .borrow()
和 .borrow_mut()
方法来获取对 Vec 的共享和可变引用(如果您可以做一些事情,这些方法还有 try_*
变体如果不可能借用是明智的。
如果您不使用 RefCell
,您将只能从 Rc
获得 immutable/shared 引用(&Vec
)(除非您只有一个 Rc
,但那样你就不需要 Rc
!)
尝试如下操作:
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"),
});