I implemented a trait to share similar logic between my structs. This trait provides a collect_responses function, which takes vector of handlers and return vector of responses from the handlers. But after I applied this trait I got an errors like:
error[E0507]: cannot move out of `input`, a captured variable in an `FnMut` closure
--> src/lib.rs:16:30
|
12 | input: HandlerInput
| ----- captured outer variable
...
16 | .map(|func| func(input))
| ------------^^^^^-
| | |
| | move occurs because `input` has type `HandlerInput<'_>`, which does not implement the `Copy` trait
| captured by this `FnMut` closure
error[E0382]: use of partially moved value: `input`
This is my trait code:
pub trait Processor {
fn process_input(input: HandlerInput) -> Result<Vec<HandlerOutput>, Error>;
fn collect_responses(
handlers: Vec<fn(HandlerInput) -> Result<HandlerOutput, Error>>,
input: HandlerInput
) -> Result<Vec<HandlerOutput>, Error> {
let responses = handlers
.iter()
.map(|func| func(input))
.filter(|r| match r {
Ok(_) => true,
_ => false,
})
.map(|r| r.unwrap())
.collect::<Vec<HandlerOutput>>();
return if responses.is_empty() {
Err(
Error::new(
ErrorKind::Other,
"No successful response"
)
)
} else {
Ok(responses)
}
}
}
This is sandbox implementation in context of my real code.
Could somebody explain, why this issue happen and how to fix it ?
fn(HandlerInput) -> Result<HandlerOutput, Error>
This is almost certainly not the type you want, for multiple reasons. First, fn is a function pointer, i.e. a literal function written at the top level in your application. It can't be a closure or some other function-like thing. fn is almost exclusively used for interfacing with C, so you, at minimum, want Fn, FnMut, or FnOnce. See that link for more details on the difference. FnOnce won't suffice because you need to call it in a map, so I'm guessing you want FnMut, but read the difference between FnMut and Fn and determine for yourself which one you need.
Second, if you write
FnMut(HandlerInput) -> Result<HandlerOutput, Error>
This is a function that takes a HandlerInput by value. It moves its argument and the caller can never use the argument again. So you certainly can't call it several times on the same handler in a .map, since the very first call moves the value and renders the variable handler unusable. If HandlerInput implements Clone, then you can write
.map(|func| func(input.clone())
But that's probably not what you want. Almost certainly, you want a reference here.
FnMut(&HandlerInput) -> Result<HandlerOutput, Error>
FnMut(&mut HandlerInput) -> Result<HandlerOutput, Error>
The first is a function which takes an immutable reference to a HandlerInput. It can read the HandlerInput but can't change, move, or drop it. If HandlerInput is a read-only data structure, then this is the best option.
The second is a function which takes a mutable reference. It can read and modify the HandlerInput but can't move or drop it. It also can't be safely used across threads. Use this if your handlers are intended to modify the input argument.
Related
As explained in Why is the move keyword needed when returning a closure which captures a Copy type? and How to copy instead of borrow an i64 into a closure in Rust?, if a closure captures a type that implements Copy, we need to use the move keyword to eagerly copy the value:
fn use_move_to_copy_into_closure() -> impl FnOnce(i32) -> bool {
let captured = 0;
move |value| value > captured
}
As of Rust 2021, disjoint capture in closures means that only the specific fields used in a closure are captured by the closure:
struct Wrapper(i32);
fn edition_2021_only_captures_specific_fields(captured: Wrapper) -> impl FnOnce(i32) -> bool {
let ret = move |value| value > captured.0;
drop(captured); // fails in 2015, 2018, succeeds in 2021
ret
}
If I capture a Copy field belonging to a reference, however, the field is not copied:
struct Wrapper(i32);
fn capturing_a_field_of_a_reference(captured: &Wrapper) -> impl FnOnce(i32) -> bool {
move |value| value > captured.0
}
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> src/lib.rs:15:60
|
15 | fn capturing_a_field_of_a_reference(captured: &Wrapper) -> impl FnOnce(i32) -> bool {
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^
| |
| hidden type `[closure#src/lib.rs:16:5: 16:36]` captures the anonymous lifetime defined here
|
help: to declare that the `impl Trait` captures `'_`, you can add an explicit `'_` lifetime bound
|
15 | fn capturing_a_field_of_a_reference(captured: &Wrapper) -> impl FnOnce(i32) -> bool + '_ {
| ++++
I expected that the field .0 would be copied in, just like in the unwrapped i32 or the owned Wrapper cases. What causes this difference?
move captures the environment by value, but things in the environment that are references remain references -- the references are captured by value.
Put another way, move closures only try to move values, because otherwise there wouldn't be a way to simultaneously capture some things by value and some things by reference. For example, it's a common pattern to do this when dealing with threads in e.g. crossbeam. Assume these structs:
#[derive(Clone)]
struct Foo;
impl Foo {
pub fn baz(&mut self, _: i32) {}
}
struct Bar(pub i32);
And this snippet:
let foo = Foo;
let bar = Bar(0);
{
let mut foo = foo.clone();
let bar = &bar;
s.spawn(move |_| {
foo.baz(bar.0);
});
}
Here the closure takes ownership of the clone of foo but references bar.0. This is true even if the type of bar.0 is Copy.
If it didn't work this way, there would be no way to express that the closure should own the foo clone, but borrow the copyable value bar.0.
Remember that implementing Copy on a type only means that an attempt to move a value of that type will instead copy. Since captured is a reference in your third example, capturing captured.0 doesn't attempt to move the i32 like it does in your second example where you have an owned value, and if a move isn't attempted then no copy can happen.
It's not a question of whether the copy occurs, but when it occurs. The copy only happens when the closure is called, not when it's defined (how could it be -- a captured hasn't been provided yet). Since the closure outlives the function call, it needs to know that its reference to captured will be valid when it's actually called precisely so that it has something to copy. Therefore it needs to be annotated with a lifetime tying it to the lifetime of captured. Thanks to lifetime inference, it's sufficient to simply add '_ to the impl instead of needing to explicitly write fn capturing_a_field_of_a_reference<'a>(captured: &'a Wrapper) -> impl 'a + FnOnce(i32) -> bool.
I'm trying to a write a function, which will take an argument of type T and return a closure, which, when called, will return that argument.
How do I write that?
pub struct ParseError {}
pub type Parser<T> = Box<dyn Fn(&str) -> Result<(&str, T), ParseError>>;
pub fn pure<T: 'static>(value: T) -> Parser<T> {
Box::new(move |input: &str| -> Result<(&str, T), ParseError> { Ok((input, value)) })
}
The error:
Checking parsec v0.1.0 (/home/arjaz/Documents/code/rust/parsec)
error[E0507]: cannot move out of `value`, a captured variable in an `Fn` closure
--> src/parsec.rs:16:79
|
12 | pub fn pure<T: 'static>(value: T) -> Parser<T>
| ----- captured outer variable
...
16 | Box::new(move |input: &str| -> Result<(&str, T), ParseError> { Ok((input, value)) })
| ^^^^^ move occurs because `value` has type `T`, which does not implement the `Copy` trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
error: could not compile `parsec`.
Here is a simplified version in the Rust playground, one that avoids Box and Parser. The function consumes value, and returns a closure that, when called, simply returns value. Note that the closure is of type FnOnce (or, more precisely, is of some type based on FnOnce), so we can only call it once. After the call, the memory associated with value belongs to the caller.
If instead we use Fn, then the closure can be called multiple times. But after the first call, the closure no longer owns value -- it was given to the first caller as the return value -- so calling the closure a second time is not going to work. Hence the error. If a closure is going to return the value multiple times, then it must return a copy or clone of value, retaining ownership of the original for itself. Since the function is generic over the type of value, T, you would need to constrain T to be Copy or Clone.
The implementation of std::mem::drop is documented to be the following:
pub fn drop<T>(_x: T) { }
As such, I would expect the closure |_| () (colloquially known as the toilet closure) to be a potential 1:1 replacement to drop, in both directions. However, the code below shows that drop isn't compatible with a higher ranked trait bound on the function's parameter, whereas the toilet closure is.
fn foo<F, T>(f: F, x: T)
where
for<'a> F: FnOnce(&'a T),
{
dbg!(f(&x));
}
fn main() {
foo(|_| (), "toilet closure"); // this compiles
foo(drop, "drop"); // this does not!
}
The compiler's error message:
error[E0631]: type mismatch in function arguments
--> src/main.rs:10:5
|
1 | fn foo<F, T>(f: F, x: T)
| ---
2 | where
3 | for<'a> F: FnOnce(&'a T),
| ------------- required by this bound in `foo`
...
10 | foo(drop, "drop"); // this does not!
| ^^^
| |
| expected signature of `for<'a> fn(&'a _) -> _`
| found signature of `fn(_) -> _`
error[E0271]: type mismatch resolving `for<'a> <fn(_) {std::mem::drop::<_>} as std::ops::FnOnce<(&'a _,)>>::Output == ()`
--> src/main.rs:10:5
|
1 | fn foo<F, T>(f: F, x: T)
| ---
2 | where
3 | for<'a> F: FnOnce(&'a T),
| ------------- required by this bound in `foo`
...
10 | foo(drop, "drop"); // this does not!
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime
Considering that drop is supposedly generic with respect to any sized T, it sounds unreasonable that the "more generic" signature fn(_) -> _ is not compatible with for<'a> fn (&'a _) -> _. Why is the compiler not admitting the signature of drop here, and what makes it different when the toilet closure is placed in its stead?
The core of the issue is that drop is not a single function, but rather a parameterized set of functions that each drop some particular type. To satisfy a higher-ranked trait bound (hereafter hrtb), you'd need a single function that can simultaneously take references to a type with any given lifetime.
We'll use drop as our typical example of a generic function, but all this applies more generally too. Here's the code for reference: fn drop<T>(_: T) {}.
Conceptually, drop is not a single function, but rather one function for every possible type T. Any particular instance of drop takes only arguments of a single type. This is called monomorphization. If a different T is used with drop, a different version of drop is compiled. That's why you can't pass a generic function as an argument and use that function in full generality (see this question)
On the other hand, a function like fn pass(x: &i32) -> &i32 {x} satisfies the hrtb for<'a> Fn(&'a i32) -> &'a i32. Unlike drop, we have a single function that simultaneously satisfies Fn(&'a i32) -> &'a i32 for every lifetime 'a. This is reflected in how pass can be used.
fn pass(x: &i32) -> &i32 {
x
}
fn two_uses<F>(f: F)
where
for<'a> F: Fn(&'a i32) -> &'a i32, // By the way, this can simply be written
// F: Fn(&i32) -> &i32 due to lifetime elision rules.
// That applies to your original example too.
{
{
// x has some lifetime 'a
let x = &22;
println!("{}", f(x));
// 'a ends around here
}
{
// y has some lifetime 'b
let y = &23;
println!("{}", f(y));
// 'b ends around here
}
// 'a and 'b are unrelated since they have no overlap
}
fn main() {
two_uses(pass);
}
(playground)
In the example, the lifetimes 'a and 'b have no relation to each other: neither completely encompasses the other. So there isn't some kind of subtyping thing going on here. A single instance of pass is really being used with two different, unrelated lifetimes.
This is why drop doesn't satisfy for<'a> FnOnce(&'a T). Any particular instance of drop can only cover one lifetime (ignoring subtyping). If we passed drop into two_uses from the example above (with slight signature changes and assuming the compiler let us), it would have to choose some particular lifetime 'a and the instance of drop in the scope of two_uses would be Fn(&'a i32) for some concrete lifetime 'a. Since the function would only apply to single lifetime 'a, it wouldn't be possible to use it with two unrelated lifetimes.
So why does the toilet closure get a hrtb? When inferring the type for a closure, if the expected type hints that a higher-ranked trait bound is needed, the compiler will try to make one fit. In this case, it succeeds.
Issue #41078 is closely related to this and in particular, eddyb's comment here gives essentially the explanation above (though in the context of closures, rather than ordinary functions). The issue itself doesn't address the present problem though. It instead addresses what happens if you assign the toilet closure to a variable before using it (try it out!).
It's possible that the situation will change in the future, but it would require a pretty big change in how generic functions are monomorphized.
In short, both lines should fail. But since one step in old way of handling hrtb lifetimes, namely the leak check, currently has some soundness issue, rustc ends up (incorrectly) accepting one and leaving the other with a pretty bad error message.
If you disable the leak check with rustc +nightly -Zno-leak-check, you'll be able to see a more sensible error message:
error[E0308]: mismatched types
--> src/main.rs:10:5
|
10 | foo(drop, "drop");
| ^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&'a &str,)>`
found type `std::ops::FnOnce<(&&str,)>`
My interpretation of this error is that the &x in the body of the foo function only has a scope lifetime confined to the said body, so f(&x) also has the same scope lifetime which can't possibly satisfy the for<'a> universal quantification required by the trait bound.
The question you present here is almost identical to issue #57642, which also has two contrasting parts.
The new way to process hrtb lifetimes is by using so-called universes. Niko has a WIP to tackle the leak check with universes. Under this new regime, both parts of issue #57642 linked above is said to all fail with far more clear diagnoses. I suppose the compiler should be able to handle your example code correctly by then, too.
Considering the following code:
fn foo<'a, T: 'a>(t: T) -> Box<Fn() -> &'a T + 'a> {
Box::new(move || &t)
}
What I expect:
The type T has lifetime 'a.
The value t live as long as T.
t moves to the closure, so the closure live as long as t
The closure returns a reference to t which was moved to the closure. So the reference is valid as long as the closure exists.
There is no lifetime problem, the code compiles.
What actually happens:
The code does not compile:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/lib.rs:2:22
|
2 | Box::new(move || &t)
| ^^
|
note: first, the lifetime cannot outlive the lifetime as defined on the body at 2:14...
--> src/lib.rs:2:14
|
2 | Box::new(move || &t)
| ^^^^^^^^^^
note: ...so that closure can access `t`
--> src/lib.rs:2:22
|
2 | Box::new(move || &t)
| ^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the function body at 1:8...
--> src/lib.rs:1:8
|
1 | fn foo<'a, T: 'a>(t: T) -> Box<Fn() -> &'a T + 'a> {
| ^^
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::ops::Fn() -> &'a T + 'a)>
found std::boxed::Box<dyn std::ops::Fn() -> &T>
I do not understand the conflict. How can I fix it?
Very interesting question! I think I understood the problem(s) at play here. Let me try to explain.
tl;dr: closures cannot return references to values captured by moving, because that would be a reference to self. Such a reference cannot be returned because the Fn* traits don't allow us to express that. This is basically the same as the streaming iterator problem and could be fixed via GATs (generic associated types).
Implementing it manually
As you probably know, when you write a closure, the compiler will generate a struct and impl blocks for the appropriate Fn traits, so closures are basically syntax sugar. Let's try to avoid all that sugar and build your type manually.
What you want is a type which owns another type and can return references to that owned type. And you want to have a function which returns a boxed instance of said type.
struct Baz<T>(T);
impl<T> Baz<T> {
fn call(&self) -> &T {
&self.0
}
}
fn make_baz<T>(t: T) -> Box<Baz<T>> {
Box::new(Baz(t))
}
This is pretty equivalent to your boxed closure. Let's try to use it:
let outside = {
let s = "hi".to_string();
let baz = make_baz(s);
println!("{}", baz.call()); // works
baz
};
println!("{}", outside.call()); // works too
This works just fine. The string s is moved into the Baz type and that Baz instance is moved into the Box. s is now owned by baz and then by outside.
It gets more interesting when we add a single character:
let outside = {
let s = "hi".to_string();
let baz = make_baz(&s); // <-- NOW BORROWED!
println!("{}", baz.call()); // works
baz
};
println!("{}", outside.call()); // doesn't work!
Now we cannot make the lifetime of baz bigger than the lifetime of s, since baz contains a reference to s which would be an dangling reference of s would go out of scope earlier than baz.
The point I wanted to make with this snippet: we didn't need to annotate any lifetimes on the type Baz to make this safe; Rust figured it out on its own and enforces that baz lives no longer than s. This will be important below.
Writing a trait for it
So far we only covered the basics. Let's try to write a trait like Fn to get closer to your original problem:
trait MyFn {
type Output;
fn call(&self) -> Self::Output;
}
In our trait, there are no function parameters, but otherwise it's fairly identical to the real Fn trait.
Let's implement it!
impl<T> MyFn for Baz<T> {
type Output = ???;
fn call(&self) -> Self::Output {
&self.0
}
}
Now we have a problem: what do we write instead of ???? Naively one would write &T... but we need a lifetime parameter for that reference. Where do we get one? What lifetime does the return value even have?
Let's check the function we implemented before:
impl<T> Baz<T> {
fn call(&self) -> &T {
&self.0
}
}
So here we use &T without lifetime parameter too. But this only works because of lifetime elision. Basically, the compiler fills in the blanks so that fn call(&self) -> &T is equivalent to:
fn call<'s>(&'s self) -> &'s T
Aha, so the lifetime of the returned reference is bound to the self lifetime! (more experienced Rust users might already have a feeling where this is going...).
(As a side note: why is the returned reference not dependent on the lifetime of T itself? If T references something non-'static then this has to be accounted for, right? Yes, but it is already accounted for! Remember that no instance of Baz<T> can ever live longer than the thing T might reference. So the self lifetime is already shorter than whatever lifetime T might have. Thus we only need to concentrate on the self lifetime)
But how do we express that in the trait impl? Turns out: we can't (yet). This problem is regularly mentioned in the context of streaming iterators -- that is, iterators that return an item with a lifetime bound to the self lifetime. In today's Rust, it is sadly impossible to implement this; the type system is not strong enough.
What about the future?
Luckily, there is an RFC "Generic Associated Types" which was merged some time ago. This RFC extends the Rust type system to allow associated types of traits to be generic (over other types and lifetimes).
Let's see how we can make your example (kinda) work with GATs (according to the RFC; this stuff doesn't work yet ☹). First we have to change the trait definition:
trait MyFn {
type Output<'a>; // <-- we added <'a> to make it generic
fn call(&self) -> Self::Output;
}
The function signature hasn't changed in the code, but notice that lifetime elision kicks in! The above fn call(&self) -> Self::Output is equivalent to:
fn call<'s>(&'s self) -> Self::Output<'s>
So the lifetime of the associated type is bound to the self lifetime. Just as we wanted! The impl looks like this:
impl<T> MyFn for Baz<T> {
type Output<'a> = &'a T;
fn call(&self) -> Self::Output {
&self.0
}
}
To return a boxed MyFn we would need to write this (according to this section of the RFC:
fn make_baz<T>(t: T) -> Box<for<'a> MyFn<Output<'a> = &'a T>> {
Box::new(Baz(t))
}
And what if we want to use the real Fn trait? As far as I understand, we can't, even with GATs. I think it's impossible to change the existing Fn trait to use GATs in a backwards compatible manner. So it's likely that the standard library will keep the less powerful trait as is. (side note: how to evolve the standard library in backwards incompatible ways to use new language features is something I wondered about a few times already; so far I haven't heard of any real plan in this regards; I hope the Rust team comes up with something...)
Summary
What you want is not technically impossible or unsafe (we implemented it as a simple struct and it works). However, unfortunately it is impossible to express what you want in the form of closures/Fn traits in Rust's type system right now. This is the same problem streaming iterators are dealing with.
With the planned GAT feature, it is possible to express all of this in the type system. However, the standard library would need to catch up somehow to make your exact code possible.
What I expect:
The type T has lifetime 'a.
The value t live as long as T.
This makes no sense. A value cannot "live as long" as a type, because a type doesn't live. "T has lifetime 'a" is a very imprecise statement, easy to misunderstand. What T: 'a really means is "instances of T must stay valid at least as long as lifetime 'a. For example, T must not be a reference with a lifetime shorter than 'a, or a struct containing such a reference. Note that this has nothing to do with forming references to T, i.e. &T.
The value t, then, lives as long as its lexical scope (it's a function parameter) says it does, which has nothing to do with 'a at all.
t moves to the closure, so the closure live as long as t
This is also incorrect. The closure lives as long as the closure does lexically. It is a temporary in the result expression, and therefore lives until the end of the result expression. t's lifetime concerns the closure not at all, since it has its own T variable inside, the capture of t. Since the capture is a copy/move of t, it is not in any way affected by t's lifetime.
The temporary closure is then moved into the box's storage, but that's a new object with its own lifetime. The lifetime of that closure is bound to the lifetime of the box, i.e. it is the return value of the function, and later (if you store the box outside the function) the lifetime of whatever variable you store the box in.
All of that means that a closure that returns a reference to its own capture state must bind the lifetime of that reference to its own reference. Unfortunately, this is not possible.
Here's why:
The Fn trait implies the FnMut trait, which in turn implies the FnOnce trait. That is, every function object in Rust can be called with a by-value self argument. This means that every function object must be still valid being called with a by-value self argument and returning the same thing as always.
In other words, trying to write a closure that returns a reference to its own captures expands to roughly this code:
struct Closure<T> {
captured: T,
}
impl<T> FnOnce<()> for Closure<T> {
type Output = &'??? T; // what do I put as lifetime here?
fn call_once(self, _: ()) -> Self::Output {
&self.captured // returning reference to local variable
// no matter what, the reference would be invalid once we return
}
}
And this is why what you're trying to do is fundamentally impossible. Take a step back, think of what you're actually trying to accomplish with this closure, and find some other way to accomplish it.
You expect the type T to have lifetime 'a, but t is not a reference to a value of type T. The function takes ownership of the variable t by argument passing:
// t is moved here, t lifetime is the scope of the function
fn foo<'a, T: 'a>(t: T)
You should do:
fn foo<'a, T: 'a>(t: &'a T) -> Box<Fn() -> &'a T + 'a> {
Box::new(move || t)
}
The other answers are top-notch, but I wanted to chime in with another reason your original code couldn't work. A big problem lies in the signature:
fn foo<'a, T: 'a>(t: T) -> Box<Fn() -> &'a T + 'a>
This says that the caller may specify any lifetime when calling foo and the code will be valid and memory-safe. That cannot possibly be true for this code. It wouldn't make sense to call this with 'a set to 'static, but nothing about this signature would prevent that.
This question already has an answer here:
Mutating the same data in multiple 'static closures
(1 answer)
Closed 4 years ago.
I'm using the libpulse_binding library, and I'm trying to obtain a sequence of SinkInputInfos from the get_sink_input_info_list function:
pub fn get_sink_input_info_list<F>(
&self,
callback: F,
) -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
where
F: FnMut(ListResult<&SinkInputInfo>) + 'static,
The function takes a callback and calls it once for each SinkInputInfo it produces. I'm trying to collect all those SinkInputInfos into a single list so I can get a clearer view of the state of the world. Irritatingly, SinkInputInfo doesn't implement Copy or Clone, so I made a custom struct and implemented From to get the useful information out of the 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,
}
}
}
However, this doesn't seem to work. I have the following code:
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"),
});
but it doesn't compile:
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: the closure has to have the 'static lifetime because libpulse_binding says so (presumably because it gets handed to the PulseAudio C API, which can then do whatever it likes with it), but sink_infos is not 'static, and the closure has to borrow sink_infos in order to append to it, which makes the closure not 'static, IIUC.
How can I make a Vec (or any container, I'm not fussy) of SinkInputInfos given a 'static closure that gets called repeatedly with a &SinkInputInfo?
The basic problem here is that you're running into 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).
You are trying to keep a reference &Vec to your Vec (so you can use it later) whilst simultaneously trying to add things to it in a closure (i.e. an &mut Vec). Rust doesn't know that you won't try to use the &Vec while the closure is using the &mut Vec, so it doesn't let you create a &mut Vec in the closure while there's still a &Vec hanging around outside the closure.
The next best thing you can do is to use an Rc. This will allow you to sidestep the compiler's borrow-checking and instead defer it to runtime. However: this means that if you try to violate the borrowing rules while your program is running, it will panic instead of being a compile-time error!
For the most part, you can treat the Rc<Vec<_>> the same as a normal Vec, since Rc implements Deref.
Since you also want to be able to mutate the Vec in order to add things to it, you also need to put it in a RefCell. This will put a lock around the Vec, making sure that you only have one &mut Vec available at once, and that if you have an &Vec, you can't have a &mut Vec (again, if you try to violate the rules, your program will panic). You can use the .borrow() and .borrow_mut() methods on RefCell to get shared and mutable references to the Vec (there are also try_* variants of these methods, if you can do something sensible if a borrow is not possible).
If you didn't use a RefCell, you would only be able to obtain immutable/shared references (&Vec) from the Rc (unless you only had one Rc, but then you wouldn't need an Rc!)
Try something like the following:
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"),
});