Why does my Fn need to be Sync when spawned by Tokio? - rust

This is the code that raises a compiler error suggesting that MyFn should be restricted with a Sync bound:
use std::future::Future;
fn spawn<MyFn, Out>(func: MyFn)
where
Out: Future + Send + 'static,
MyFn: Fn() -> Out + Send + 'static,
{
tokio::spawn(call_func(func));
}
async fn call_func<MyFn, Out>(func: MyFn)
where
MyFn: Fn() -> Out,
Out: Future,
{
func().await;
func().await;
}
Link to the Playground.
This raises the following error:
Compiling playground v0.0.1 (/playground)
error: future cannot be sent between threads safely
--> src/main.rs:8:18
|
8 | tokio::spawn(call_func(func));
| ^^^^^^^^^^^^^^^ future returned by `call_func` is not `Send`
|
note: future is not `Send` as this value is used across an await
--> src/main.rs:16:11
|
16 | func().await;
| ---- ^^^^^^ await occurs here, with `func` maybe used later
| |
| has type `&MyFn` which is not `Send`
note: `func` is later dropped here
--> src/main.rs:16:17
|
16 | func().await;
| ^
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/main.rs:16:5
|
16 | func().await;
| ^^^^^^
note: required by a bound in `tokio::spawn`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.21.2/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
help: consider dereferencing here
|
8 | tokio::spawn(*call_func(func));
| +
help: consider further restricting this bound
|
6 | MyFn: Fn() -> Out + Send + 'static + std::marker::Sync,
| +++++++++++++++++++
error: could not compile `playground` due to previous error
Why exaclty does MyFn need to be restricted using the Sync bound, when all that tokio::spawn requires is the following?
pub fn spawn<T>(future: T) -> JoinHandle<T::Output>ⓘ
where
T: Future + Send + 'static,
T::Output: Send + 'static,

In this part of your code:
async fn call_func<MyFn, Out>(func: MyFn)
where
MyFn: Fn() -> Out,
Out: Future,
{
func().await;
func().await;
}
you are calling func twice. The way this is possible — why it doesn't violate Rust's move semantics even though MyFn does not have a Copy bound — is that func is being called by & reference, since it is a Fn and the definition of Fn is that you can call a function if you have & to it. Then, since there is an await in the middle, this code might move between threads. So, references to the value func is being used from two different threads, so &MyFn: Send is required, so MyFn: Sync is required.
Here's one trick to solve the problem: declare the function as FnMut instead of Fn. This means that a &mut reference is understood to be required to call the function, and since &mut references are unique they don't require Send of their referent. These changes compile successfully:
fn spawn<MyFn, Out>(func: MyFn)
where
Out: Future + Send + 'static,
MyFn: FnMut() -> Out + Send + 'static,
{
tokio::spawn(call_func(func));
}
async fn call_func<MyFn, Out>(mut func: MyFn)
where
MyFn: FnMut() -> Out,
Out: Future,
{
func().await;
func().await;
}
In principle, the compiler could observe that the internal &MyFn never needs to move between threads, and do the equivalent of my FnMut rewrite internally (every Fn is also a FnMut), but evidently it doesn't.

Related

How do I wrap a closure which returns a Future, without it being Sync?

I have the following code, in an attempt to wrap a function that returns a future:
use std::future::Future;
use std::pin::Pin;
pub type BoxedOperation = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + 'static>;
fn create_func<L, R>(func: L) -> BoxedOperation
where L: Fn() -> R + Clone + Send + 'static,
R: Future<Output = ()> + Send + 'static
{
Box::new(move || {
let func = func.clone();
Box::pin(async move {
// My logic before
(func)().await; // In the future, func will receive params
// my logic after
})
})
}
This code doesn't compile-
--> src/main.rs:14:9
|
14 | / Box::pin(async move {
15 | | (func)().await;
16 | | })
| |__________^ future created by async block is not `Send`
|
note: future is not `Send` as this value is used across an await
--> src/main.rs:15:21
|
15 | (func)().await;
| ------ ^^^^^^ await occurs here, with `(func)` maybe used later
| |
| has type `&L` which is not `Send`
note: `(func)` is later dropped here
--> src/main.rs:15:27
|
15 | (func)().await;
| ^
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/main.rs:15:13
|
15 | (func)().await;
| ^^^^^^^^
= note: required for the cast from `impl Future<Output = ()>` to the object type `dyn Future<Output = ()> + Send`
help: consider further restricting this bound
|
9 | where L: Fn() -> R + Clone + Send + 'static + std::marker::Sync,
|
+++++++++++++++++++
Now it's critical for the BoxedOperation here to return a future that is Send so I can tokio::spawn it later on, and I also don't want it to be Sync as that would require all everything used in func to be Sync as well. I don't really understand why this isn't Send in the first place.
This is a tricky case.
When you call func, the compiler uses Fn::call() that takes self by reference. So the compiler borrows func, creating &L, and calls this type. But as generator/async functions captures are not precise, this type ends up being included in the resulting future even though it is not needed, and so the resulting future requires &L: Send -> L: Sync in order to be Send.
To fix the problem, you can force the compiler to drop the borrow immediately before awaiting the returned future:
fn create_func<L, R>(func: L) -> BoxedOperation
where
L: Fn() -> R + Clone + Send + 'static,
R: Future<Output = ()> + Send + 'static,
{
Box::new(move || {
let func = func.clone();
Box::pin(async move {
let fut = { func() }; // Notice the block.
fut.await;
})
})
}

Returning a higher-kinded closure that captures a reference

I'm trying to write a method on a struct that returns a closure. This closure should take a &[u8] with an arbitrary lifetime 'inner as an argument and return the same type, &'inner [u8]. To perform its function, the closure also needs a (shared) reference to a member of the struct &self. Here is my code:
#![warn(clippy::pedantic)]
// placeholder for a large type that I can't afford to clone
struct Opaque(usize);
struct MyStruct<'a>(Vec<&'a Opaque>);
impl<'a> MyStruct<'a> {
pub fn get_func_for_index(
&'a self,
n: usize,
) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
Some(move |i: &[u8]| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
})
}
}
fn main() {
let o1 = Opaque(1);
let o5 = Opaque(5);
let o7 = Opaque(7);
let x = MyStruct(vec![&o1, &o5, &o7]);
let drop_five = x.get_func_for_index(1 /*Opaque(5)*/).unwrap();
let data: Vec<u8> = Vec::from(&b"testing"[..]);
assert_eq!(drop_five(&data[..]), b"ng");
}
If I try to compile it with rustc 1.54.0 (a178d0322 2021-07-26), I get the following error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 14:14...
--> /tmp/lifetimes.rs:14:14
|
14 | Some(move |i: &[u8]| {
| ______________^
15 | | // placeholder: do something with the input using the &Opaque
16 | | &i[opaque.0..]
17 | | })
| |_________^
note: ...so that reference does not outlive borrowed content
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
--> /tmp/lifetimes.rs:6:6
|
6 | impl<'a> MyStruct<'a> {
| ^^
note: ...so that return value is valid for the call
--> /tmp/lifetimes.rs:10:17
|
10 | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> /tmp/lifetimes.rs:7:5
|
7 | / pub fn get_func_for_index(
8 | | &'a self,
9 | | n: usize,
10 | | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| |_______________________________________________________________________^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
It's quite a mouthful and I don't really understand what it's trying to tell me. The first part (first, the lifetime...) makes sense to me, the returned slice must not outlive the closure argument. The second part (but, the lifetime...) however seems weird to me - the + 'a annotation in the method signature refers to the closure itself (because it captures &'a self.foo), not to the value the closure returns.
Is it possible to change the code to model this correctly in rust, or is this construct just not supported at this time?
The problem is here:
Some(move |i: &[u8]| {
Every & has a lifetime on it, explicit or not. What is the lifetime of &[u8]? Clearly it should be "a lifetime chosen by the caller of the closure" (that is, a higher-ranked lifetime). But when the compiler encounters a reference type with a free lifetime parameter, even in the argument list of a closure, it will not assume that the lifetime is higher-ranked. The error message you get about the "anonymous lifetime #1" is the compiler confusedly trying to make a non-higher-ranked lifetime work.
The compiler could, in theory, work "backwards" from the impl Fn in the return type and recognize that the type of the closure needs to have this higher ranked lifetime. It's not quite clever enough to do that in this case, but there is a way to convince it: use a local function with a bounded type parameter to constrain the type of the closure to exactly the bound you want.
pub fn get_func_for_index(
&self, // note 1
n: usize,
) -> Option<impl 'a + for<'inner> Fn(&'inner [u8]) -> &'inner [u8]> { // note 2
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
// helper function to constrain the closure type
fn check<F: Fn(&[u8]) -> &[u8]>(f: F) -> F { // note 3
f
}
Some(check(move |i| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
}))
}
Playground
Please note the following:
&'a self is too conservative for this function because 'a is the lifetime parameter of the reference self contains, not the lifetime for which self is borrowed. In general, you should almost never write &'a self or &'a mut self where 'a is a named lifetime from an outer scope.
I find the + 'a easy to miss at the end of a long trait, especially a Fn trait with a return type. I recommend fronting the lifetime (putting it first) in cases like this to make clear that it relates more to the impl than to the &'inner [u8]. This is a stylistic choice.
Fn(&[u8]) -> &[u8] is actually exactly the same as for<'inner> Fn(&'inner [u8]) -> &'inner [u8], because the elision rules for Fn traits are the same as for regular functions. Either way is fine; I find the shorter version easier to read.
Similar questions
Expected bound lifetime parameter, found concrete lifetime [E0271]
How to declare a higher-ranked lifetime for a closure argument?
Why does this closure require inlining or `dyn`? What does `dyn` do here?

Specify Rust closures lifetime

I was making the executor/reactor while discovered this a lifetime problem. It is not related to async/Future and can be reproduced without async sugar.
use std::future::Future;
struct Runtime;
fn start_with_runtime<C, F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}
async fn async_main(_rt: &Runtime) {
// I can use _rt to do async stuff here
}
fn main() {
start_with_runtime(|rt| { async_main(rt) });
}
I would like to the start_with_runtime() to run the future and provide the async Runtime reference as parameter.
It does not compile:
error: lifetime may not live long enough
--> src/main.rs:17:31
|
17 | start_with_runtime(|rt| { async_main(rt) });
| --- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is impl std::future::Future
| has type `&'1 Runtime`
I think that this problem seems to be because how rust infers lifetimes for closures:
https://github.com/rust-lang/rust/issues/58052 :
fn main() {
let f = |x: &i32| x;
let i = &3;
let j = f(i);
}
Does not compiles either:
error: lifetime may not live long enough
--> src/main.rs:2:23
|
2 | let f = |x: &i32| x;
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`
Looks like my closure signature inferred as |&'a Runtime| -> impl Future + 'b and thus the lifetime error. I feel that given correct expected signature for closure would help, but how do I provide the correct signature in start_with_runtime?
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),
Does not work because impl Trait is not allowed here.
fn start_with_runtime<C,F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future + 'a
Does not work as well because 'a is not known outside of HRTB expression.
It works if I know the type:
struct MyType<'a> {
_rt: &'a Runtime
}
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,
This is kind of sad when you've thought through all the lifetimes but language does not provide a way to express this. Perhaps there is a trick in rust to make this work?
There seem to be two different questions in this one: can the required relationship be expressed in Rust syntax and will it work with closure type inference or not.
Let's start with the first one. You're right that this cannot be expressed with just where clauses. To express this one needs to add a helper trait
trait BorrowingFn<'a> {
type Fut: std::future::Future<Output = Something> + 'a;
fn call(self, arg: &'a Runtime) -> Self::Fut;
}
that allows the bound we need to be written as
C: for<'a> BorrowingFn<'a>,
and provide a blanket implementation of this trait for all applicable functions
impl<'a, Fu: 'a, F> BorrowingFn<'a> for F
where
F: FnOnce(&'a Runtime) -> Fu,
Fu: std::future::Future<Output = ()> + 'a,
{
type Fut = Fu;
fn call(self, rt: &'a Runtime) -> Fu {
self(rt)
}
}
(playground)
Ok, so it works with an async function, but does it work with a closure that needs type inference? Unfortunately, the answer is "no"
error: implementation of `BorrowingFn` is not general enough
--> src/main.rs:33:5
|
5 | / trait BorrowingFn<'a> {
6 | | type Fut: std::future::Future<Output = ()> + 'a;
7 | | fn call(self, arg: &'a Runtime) -> Self::Fut;
8 | | }
| |_- trait `BorrowingFn` defined here
...
33 | start_with_runtime(|rt| async_main(rt)); // however, it does not work with closure type inference :-(
| ^^^^^^^^^^^^^^^^^^ implementation of `BorrowingFn` is not general enough
|
= note: `[closure#src/main.rs:33:24: 33:43]` must implement `BorrowingFn<'0>`, for any lifetime `'0`...
= note: ...but `[closure#src/main.rs:33:24: 33:43]` actually implements `BorrowingFn<'1>`, for some specific lifetime `'1`
This is being tracked in rust-lang/rust#70263. The compiler is not smart enough yet to notice that this closure needs a higher-rank type.
For fun I tried compiling with -Z chalk on Nightly, but it's not yet ready for this (internal compiler error).
Sorry, this is a limitation in the language. You can only specify the lifetime on concrete types. One workaround is to use the trait object type.
fn start_with_runtime<C, F, T>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> Pin<Box<dyn Future<Item = T> + Send + 'a>>,
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}

Why can't I call a method after destructuring a variable but I can if I access the field directly?

The following didn't compile:
use std::any::Any;
pub trait CloneBox: Any {
fn clone_box(&self) -> Box<dyn CloneBox>;
}
impl<T> CloneBox for T
where
T: Any + Clone,
{
fn clone_box(&self) -> Box<dyn CloneBox> {
Box::new(self.clone())
}
}
struct Foo(Box<dyn CloneBox>);
impl Clone for Foo {
fn clone(&self) -> Self {
let Foo(b) = self;
Foo(b.clone_box())
}
}
Error message:
error[E0495]: cannot infer an appropriate lifetime for pattern due to conflicting requirements
--> src/lib.rs:20:17
|
20 | let Foo(b) = self;
| ^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 19:5...
--> src/lib.rs:19:5
|
19 | / fn clone(&self) -> Self {
20 | | let Foo(b) = self;
21 | | Foo(b.clone_box())
22 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:20:17
|
20 | let Foo(b) = self;
| ^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `&std::boxed::Box<dyn CloneBox>` will meet its required lifetime bounds
--> src/lib.rs:21:15
|
21 | Foo(b.clone_box())
|
However if change the code in clone() from Foo(b.clone_box()) to Foo(self.0.clone_box()), it compiles without problem. In theory, field access should be the same as pattern matching, but why does pattern matching have lifetime issues?
In my real code, the data is in an enum, not a struct, so pattern matching is the only option.
TL;DR: Dereference the value before calling the method:
Foo((*b).clone_box())
With let Foo(b) = self, the type of b is &Box<(dyn CloneBox + 'static)>. The method call is effectively
Foo(<&Box<dyn CloneBox + 'static> as CloneBox>::clone_box(&b))
This value cannot be made into the trait object Box<dyn CloneBox + 'static> because of the local reference. Amusingly, I believe this would be recursively using the blanket implementation if the compiler allowed it.
With self.0.clone_box(), the method call is effectively:
Foo(<dyn CloneBox as CloneBox>::clone_box(&**b)
We could write this as Foo((&**b).clone_box()) to be explicit, but since there's no intermediate implementations, Foo((*b).clone_box()) is sufficient.
See also:
What are Rust's exact auto-dereferencing rules?

Returning non-trivial gstreamer "pad callbacks" as boxed closures

I'm trying to write a factory function for the creation of closures for use as 'pad callbacks' in gstreamer. I've provided a stripped down example that should compile with the gstreamer crate and gstreamer binaries/plugins installed.
Through my research, I've gotten the factory function to work by using the 'impl trait' method, instead of boxing. I'd like to figure out the boxed method though, as it seems more appropriate in some situations.
This is as close as I've gotten. The problem can be seen by uncommenting the portion labeled Closure function using 'Box<>'. I've tried specifying the Fn portion as a type parameter with a where clause, as well as many other attempts. In this attempt, it looks like the issue is that I can't unbox the closure function to use as assignment to a local variable, or as use in the add_probe callback due to requiring compile-time size, which is the whole reason for the box in the first place...
Ctrl+C or 'exit\n' from stdin should close the program.
extern crate gstreamer as gst;
use gst::prelude::*;
use std::io;
fn create_impl_probe_fn(
x: i32,
) -> impl Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
move |_, _| {
println!("Idle... {}", x);
gst::PadProbeReturn::Pass
}
}
fn create_boxed_probe_fn(
x: i32,
) -> Box<Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static> {
Box::new(move |_, _| {
println!("Idle... {}", x);
gst::PadProbeReturn::Pass
})
}
fn main() {
println!("Starting...");
//TODO Pass args to gst?
gst::init().unwrap();
//GStreamer
let parse_line = "videotestsrc ! autovideosink name=mysink";
let pipeline = gst::parse_launch(parse_line).unwrap();
let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure);
//Inline closure
let mut x = 1;
pipeline
.clone()
.dynamic_cast::<gst::Bin>()
.unwrap()
.get_by_name("mysink")
.unwrap()
.get_static_pad("sink")
.unwrap()
.add_probe(gst::PadProbeType::BLOCK, move |_, _| {
println!("Idle... {}", x);
gst::PadProbeReturn::Pass
});
//Closure function using 'impl'
x = 20;
let impl_probe_fn = create_impl_probe_fn(x);
//TEMP Test
pipeline
.clone()
.dynamic_cast::<gst::Bin>()
.unwrap()
.get_by_name("mysink")
.unwrap()
.get_static_pad("sink")
.unwrap()
.add_probe(gst::PadProbeType::BLOCK, impl_probe_fn);
/*
//Closure function using 'Box<>'
x = 300;
let boxed_probe_fn = create_boxed_probe_fn(x);
//TEMP Test
pipeline
.clone()
.dynamic_cast::<gst::Bin>()
.unwrap()
.get_by_name("mysink")
.unwrap()
.get_static_pad("sink")
.unwrap()
.add_probe(gst::PadProbeType::BLOCK, *boxed_probe_fn);
*/
//Input Loop
loop {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
match input.trim() {
"exit" => break,
"info" => {
let (state_change_return, cur_state, old_state) =
pipeline.get_state(gst::CLOCK_TIME_NONE);
println!(
"Pipeline Info: {:?} {:?} {:?}",
state_change_return, cur_state, old_state
);
}
"pause" => {
let _ = pipeline.set_state(gst::State::Paused);
println!("Pausing");
}
"resume" => {
let _ = pipeline.set_state(gst::State::Playing);
println!("Resuming");
}
_ => println!("Unrecognized command: '{}'", input.trim()),
}
println!("You've entered: {}", input.trim());
}
//Shutdown
let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure);
println!("Shutting down streamer");
}
I know several similar questions exist on the net and here at SO, but I can't seem to figure how to apply any of the solutions to this specific function. I've included "non-trivial" and "gstreamer" in the title to differentiate.
[EDIT]
Sorry, here's a bit more info... just didn't want to muddy the water or complicate the issue...
I can't post everything I've tried. It's well over 10+ hours of small changes/attempts and many tabs. I can reproduce a few attempts that seemed close, or that I expected to work.
The above Box attempt is how I figured it would work based on the information here:
https://doc.rust-lang.org/1.4.0/book/closures.html
https://doc.rust-lang.org/book/second-edition/ch19-05-advanced-functions-and-closures.html (This one doesn't close over any stack values, and so doesn't have the 'move'.)
Rust closures from factory functions (More that makes me feel like what I have should work...)
Box<> section of the rust book: https://doc.rust-lang.org/book/second-edition/ch15-01-box.html
Here's the add_probe signature: https://sdroege.github.io/rustdoc/gstreamer/gstreamer/trait.PadExtManual.html#tymethod.add_probe
Here's the error from the above (with offending add_probe call uncommented):
error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
--> src/main.rs:63:14
|
63 | .add_probe(gst::PadProbeType::BLOCK, *boxed_probe_fn);
| ^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`
So, I guess since the size of the closure isn't known at compile-time, I can't pass it as an argument?
Changing the dereference to be on the assignement line above the '.add_probe' gives a similar error:
error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
--> src/main.rs:57:13
|
57 | let boxed_probe_fn = *create_boxed_probe_fn(x);
| ^^^^^^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`
= note: all local variables must have a statically known size
error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
--> src/main.rs:63:14
|
63 | .add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);
| ^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`
I understand the need for a stack based binding to need a compile-time size.... so this almost feels imposible to do unless the add_probe function itself took a Boxed<> arguement?
Onto some more attempts. Several places, including the add_probe function signature itself use a Type parameter and the 'where' clause to specify the Fn trait.
add_probe declaration: https://github.com/sdroege/gstreamer-rs/blob/db3fe694154c697afdaf3efb6ec65332546942e0/gstreamer/src/pad.rs
Post recomending using the 'where' clause: Sized is not implemented for the type Fn
So, let's try that, changing the create_boxed_probe_fn to:
fn create_boxed_probe_fn<F>(x: i32) -> Box<F>
where F: Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
Box::new(move |_, _| {
println!("Idle... {}", x);
gst::PadProbeReturn::Pass
})
}
Error:
error[E0308]: mismatched types
--> src/main.rs:15:18
|
15 | Box::new(move |_, _| {
| __________________^
16 | | println!("Idle... {}", x);
17 | |
18 | | gst::PadProbeReturn::Pass
19 | | })
| |_________^ expected type parameter, found closure
|
= note: expected type `F`
found type `[closure#src/main.rs:15:18: 19:10 x:_]`
This seems to be because we've specified the type above, but a closure ofcourse is it's own type. Trying the following doesn't work, as it's a trait, and can't be cast with 'as':
fn create_boxed_probe_fn<F>(x: i32) -> Box<F>
where F: Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
Box::new(move |_, _| {
println!("Idle... {}", x);
gst::PadProbeReturn::Pass
} as F)
}
Error:
error[E0308]: mismatched types
--> src/main.rs:15:18
|
15 | Box::new(move |_, _| {
| __________________^
16 | | println!("Idle... {}", x);
17 | |
18 | | gst::PadProbeReturn::Pass
19 | | } as F)
| |______________^ expected type parameter, found closure
|
= note: expected type `F`
found type `[closure#src/main.rs:15:18: 19:15 x:_]`
error[E0605]: non-primitive cast: `gst::PadProbeReturn` as `F`
--> src/main.rs:15:30
|
15 | Box::new(move |_, _| {
| ______________________________^
16 | | println!("Idle... {}", x);
17 | |
18 | | gst::PadProbeReturn::Pass
19 | | } as F)
| |______________^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
It mentions the 'From' trait. I've not looked into that, because implimenting traits for a closure doesn't seem right. I'm not even sure it's possible?
I also tried what they seem to call type ascription (Instead of 'as F' using ':F'), but this seem unsupported at this time: https://github.com/rust-lang/rust/issues/23416
This guy had the same issue, but it seems like his solution was to not use a type parameter and instead specify the Fn portion without the where clause. (Which is my top non-working attempt.) Not entirely sure, since he doesn't post what he did to fix it. https://github.com/rust-lang/rust/issues/51154
No amount of adding the impl keyword to any of the box versions seems to help. The syntax for using it like I do in the unboxed "working" version seems new and I haven't found great documentation on it. Here's some info on it: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
More links that are related:
How do I store a closure in Rust?
Closure in the return type for a Rust function
expected trait core::ops::FnMut, found type parameter
https://doc.rust-lang.org/std/boxed/trait.FnBox.html
Since Rust 1.35
Box<dyn Fn(...)> implements Fn(...), so you can simply pass the Box directly to add_probe:
.add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);
Original answer
This problem can be reduced to a surprisingly concise example:
fn call<F: Fn()>(f: F) {
f();
}
fn main() {
let g = || (); // closure that takes nothing and does nothing
let h = Box::new(|| ()) as Box<dyn Fn()>; // that but as a Fn() trait object
call(g); // works
call(h); // fails
}
The heart of the problem is that Box<dyn Fn()> does not implement
Fn(). There's no good reason this doesn't work, but there are a
collection of factors that make it awkward to fix:
It's not possible to call a method that takes self by value on a trait object. This makes it not possible to call a Box<dyn FnOnce()>. The current workaround is to use Box<dyn FnBox>, which
does implement FnOnce() (but this does not directly apply to your situation or the example above, since you want to use Fn).
Despite this, it may one day become possible
to call a Box<dyn FnOnce()>, so FnBox is in a sort of Limbo where
people don't want to fix or stabilize it to work around a temporary
issue.
Adding an impl to core to make Fn() work may nevertheless conflict with FnBox in ways that I don't quite grasp. There are
several comments about this on issue
#28796.
It's possible that implementing Fn() for Box<dyn Fn()> just can't
be done in the language as is. It's also possible that it could be
done, but is a bad idea for forwards compatibility reasons; and it's
also possible that it could be done and is a good idea, but nobody has
done it yet. That said, with things as they are now, you have a couple
of mostly unpleasant options.
As someone suggested in the question comments, you could make your own
wrapper struct that wraps a closure, and implement Fn() for
Box<Wrapper<F>>.
You could make your own trait ProbeFn, which is implemented for
any closure of the correct type, and implement Fn() for Box<dyn ProbeFn>.
In some cases, you may be able to use a &dyn Fn() instead of a
Box<dyn Fn()>. This works in the above example:
call(&*h);
Unlike Box<dyn Fn()>, &dyn Fn() does implement Fn(). It's not
as general, though, because obviously it doesn't have ownership.
However, it does work on the stable compiler -- implementing Fn()
yourself requires unstable.

Resources