将 non-trivial gstreamer "pad callbacks" 作为盒装闭包返回
Returning non-trivial gstreamer "pad callbacks" as boxed closures
我正在尝试编写一个工厂函数来创建闭包,以便在 gstreamer 中用作 'pad callbacks'。我提供了一个精简的示例,应该在安装 the gstreamer crate 和 gstreamer binaries/plugins 的情况下进行编译。
通过我的研究,我已经通过使用 'impl trait' 方法而不是装箱来使工厂函数工作。不过,我想弄清楚盒装方法,因为它在某些情况下似乎更合适。
这是我得到的最接近结果。通过取消注释标记为 Closure function using 'Box<>'
的部分可以看到该问题。我已经尝试将 Fn
部分指定为带有 where clause
的类型参数,以及许多其他尝试。在这次尝试中,问题似乎是我无法将闭包函数拆箱以用作对局部变量的赋值,或者由于需要 compile-time 大小而在 add_probe
回调中使用,这首先是盒子的全部原因...
Ctrl+C 或来自标准输入的 'exit\n' 应该关闭程序。
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");
}
我知道网上和 SO 上存在几个类似的问题,但我似乎无法弄清楚如何将任何解决方案应用于此特定功能。我在标题中加入了 "non-trivial" 和 "gstreamer" 来区分。
[编辑]
抱歉,这里有更多信息...只是不想搅浑水或使问题复杂化...
我无法 post 我尝试过的一切。超过 10 小时的小 changes/attempts 和许多选项卡。我可以重现一些看起来很接近或我期望成功的尝试。
上面的 Box 尝试是我根据此处的信息认为它可以工作的方式:
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(这个不会关闭任何堆栈值,因此没有 'move'。)
(更多让我觉得我拥有的东西应该有用...)
盒子<>锈书部分:https://doc.rust-lang.org/book/second-edition/ch15-01-box.html
这是 add_probe 签名:https://sdroege.github.io/rustdoc/gstreamer/gstreamer/trait.PadExtManual.html#tymethod.add_probe
这是上面的错误(违规的 add_probe 调用未注释):
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`
所以,我想由于闭包的大小在 compile-time 处未知,我不能将其作为参数传递?
将取消引用更改为位于“.add_probe”上方的分配行会产生类似的错误:
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`
我理解基于堆栈的绑定需要 compile-time 大小....所以这几乎感觉不可能做到,除非 add_probe 函数本身采用 Boxed<> 争论?
继续尝试。几个地方,包括 add_probe 函数签名本身使用 Type 参数和 'where' 子句来指定 Fn 特征。
add_probe 声明:https://github.com/sdroege/gstreamer-rs/blob/db3fe694154c697afdaf3efb6ec65332546942e0/gstreamer/src/pad.rs
Post 推荐使用 'where' 子句:
所以,让我们尝试一下,将 create_boxed_probe_fn 更改为:
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[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:_]`
这似乎是因为我们在上面指定了类型,但是闭包当然是它自己的类型。尝试以下是行不通的,因为它是一个特征,不能用 '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[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
它提到了 'From' 特征。我没有对此进行调查,因为闭包的隐含特征似乎不正确。我什至不确定这是否可能?
我也尝试过他们所谓的类型归属(而不是 'as F' 使用':F'),但目前似乎不受支持:https://github.com/rust-lang/rust/issues/23416
这个人遇到了同样的问题,但他的解决方案似乎是不使用类型参数,而是在没有 where 子句的情况下指定 Fn 部分。 (这是我的最高 non-working 尝试。)不完全确定,因为他没有 post 他做了什么来修复它。 https://github.com/rust-lang/rust/issues/51154
将 impl 关键字添加到任何 box 版本似乎都无济于事。像我在未装箱的 "working" 版本中那样使用它的语法似乎是新的,我还没有找到很好的文档。这里有一些关于它的信息:https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
更多相关链接:
自 Rust 1.35
Box<dyn Fn(...)>
实现了 Fn(...)
,所以你可以简单地将 Box
直接传递给 add_probe
:
.add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);
原回答
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.
我正在尝试编写一个工厂函数来创建闭包,以便在 gstreamer 中用作 'pad callbacks'。我提供了一个精简的示例,应该在安装 the gstreamer crate 和 gstreamer binaries/plugins 的情况下进行编译。
通过我的研究,我已经通过使用 'impl trait' 方法而不是装箱来使工厂函数工作。不过,我想弄清楚盒装方法,因为它在某些情况下似乎更合适。
这是我得到的最接近结果。通过取消注释标记为 Closure function using 'Box<>'
的部分可以看到该问题。我已经尝试将 Fn
部分指定为带有 where clause
的类型参数,以及许多其他尝试。在这次尝试中,问题似乎是我无法将闭包函数拆箱以用作对局部变量的赋值,或者由于需要 compile-time 大小而在 add_probe
回调中使用,这首先是盒子的全部原因...
Ctrl+C 或来自标准输入的 'exit\n' 应该关闭程序。
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");
}
我知道网上和 SO 上存在几个类似的问题,但我似乎无法弄清楚如何将任何解决方案应用于此特定功能。我在标题中加入了 "non-trivial" 和 "gstreamer" 来区分。
[编辑] 抱歉,这里有更多信息...只是不想搅浑水或使问题复杂化...
我无法 post 我尝试过的一切。超过 10 小时的小 changes/attempts 和许多选项卡。我可以重现一些看起来很接近或我期望成功的尝试。
上面的 Box 尝试是我根据此处的信息认为它可以工作的方式: 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(这个不会关闭任何堆栈值,因此没有 'move'。)
盒子<>锈书部分:https://doc.rust-lang.org/book/second-edition/ch15-01-box.html
这是 add_probe 签名:https://sdroege.github.io/rustdoc/gstreamer/gstreamer/trait.PadExtManual.html#tymethod.add_probe
这是上面的错误(违规的 add_probe 调用未注释):
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`
所以,我想由于闭包的大小在 compile-time 处未知,我不能将其作为参数传递?
将取消引用更改为位于“.add_probe”上方的分配行会产生类似的错误:
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`
我理解基于堆栈的绑定需要 compile-time 大小....所以这几乎感觉不可能做到,除非 add_probe 函数本身采用 Boxed<> 争论?
继续尝试。几个地方,包括 add_probe 函数签名本身使用 Type 参数和 'where' 子句来指定 Fn 特征。
add_probe 声明:https://github.com/sdroege/gstreamer-rs/blob/db3fe694154c697afdaf3efb6ec65332546942e0/gstreamer/src/pad.rs
Post 推荐使用 'where' 子句:
所以,让我们尝试一下,将 create_boxed_probe_fn 更改为:
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[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:_]`
这似乎是因为我们在上面指定了类型,但是闭包当然是它自己的类型。尝试以下是行不通的,因为它是一个特征,不能用 '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[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
它提到了 'From' 特征。我没有对此进行调查,因为闭包的隐含特征似乎不正确。我什至不确定这是否可能?
我也尝试过他们所谓的类型归属(而不是 'as F' 使用':F'),但目前似乎不受支持:https://github.com/rust-lang/rust/issues/23416
这个人遇到了同样的问题,但他的解决方案似乎是不使用类型参数,而是在没有 where 子句的情况下指定 Fn 部分。 (这是我的最高 non-working 尝试。)不完全确定,因为他没有 post 他做了什么来修复它。 https://github.com/rust-lang/rust/issues/51154
将 impl 关键字添加到任何 box 版本似乎都无济于事。像我在未装箱的 "working" 版本中那样使用它的语法似乎是新的,我还没有找到很好的文档。这里有一些关于它的信息:https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
更多相关链接:
自 Rust 1.35
Box<dyn Fn(...)>
实现了 Fn(...)
,所以你可以简单地将 Box
直接传递给 add_probe
:
.add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);
原回答
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 implementFn()
. 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 aBox<dyn FnOnce()>
. The current workaround is to useBox<dyn FnBox>
, which does implementFnOnce()
(but this does not directly apply to your situation or the example above, since you want to useFn
).- Despite this, it may one day become possible to call a
Box<dyn FnOnce()>
, soFnBox
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 makeFn()
work may nevertheless conflict withFnBox
in ways that I don't quite grasp. There are several comments about this on issue #28796.It's possible that implementing
Fn()
forBox<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()
forBox<Wrapper<F>>
.You could make your own trait
ProbeFn
, which is implemented for any closure of the correct type, and implementFn()
forBox<dyn ProbeFn>
.In some cases, you may be able to use a
&dyn Fn()
instead of aBox<dyn Fn()>
. This works in the above example:call(&*h);
Unlike
Box<dyn Fn()>
,&dyn Fn()
does implementFn()
. It's not as general, though, because obviously it doesn't have ownership. However, it does work on the stable compiler -- implementingFn()
yourself requires unstable.