【发布时间】:2018-11-26 17:15:51
【问题描述】:
我正在尝试编写一个工厂函数来创建在 gstreamer 中用作“pad 回调”的闭包。我提供了一个精简的示例,应该在安装 the gstreamer crate 和 gstreamer 二进制文件/插件的情况下进行编译。
通过我的研究,我已经通过使用“impl trait”方法而不是装箱来让工厂函数工作。不过,我想弄清楚盒装方法,因为它在某些情况下似乎更合适。
这是我得到的最接近的结果。通过取消注释标记为Closure function using 'Box<>' 的部分可以看到问题。我尝试将Fn 部分指定为带有where clause 的类型参数,以及许多其他尝试。在此尝试中,问题似乎是我无法将闭包函数拆箱以用作对局部变量的赋值,或者由于需要编译时大小而在 add_probe 回调中使用,这就是全部原因首先是盒子...
Ctrl+C 或 'exit\n' from stdin 应该关闭程序。
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”来区分。
[编辑] 抱歉,这里有更多信息...只是不想弄脏水或使问题复杂化...
我无法发布我尝试过的所有内容。这是超过 10 多个小时的小更改/尝试和许多选项卡。我可以重现一些看起来很接近的尝试,或者我预计会奏效的尝试。
上面的 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(这个不会关闭任何堆栈值,因此没有“移动”。)
Rust closures from factory functions(更多让我觉得我应该工作的东西......)
盒子部分锈书: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`
所以,我猜由于在编译时不知道闭包的大小,所以我不能将它作为参数传递?
将取消引用更改为“.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`
我了解基于堆栈的绑定需要一个编译时大小......所以这几乎感觉不可能做到,除非 add_probe 函数本身采用 Boxed 争论?
进行更多尝试。几个地方,包括 add_probe 函数签名本身使用 Type 参数和 'where' 子句来指定 Fn trait。
add_probe 声明:https://github.com/sdroege/gstreamer-rs/blob/db3fe694154c697afdaf3efb6ec65332546942e0/gstreamer/src/pad.rs
使用“where”子句发布推荐:Sized is not implemented for the type Fn
所以,让我们尝试一下,将 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”特征。我没有对此进行研究,因为为闭包隐含特征似乎不正确。我什至不确定这是否可能?
我还尝试了他们似乎称之为类型归属的方法(而不是使用 ':F' 的 'as F'),但目前似乎不受支持:https://github.com/rust-lang/rust/issues/23416
这个人也有同样的问题,但他的解决方案似乎是不使用类型参数,而是指定不带 where 子句的 Fn 部分。 (这是我最失败的尝试。)不完全确定,因为他没有发布他为修复它所做的工作。 https://github.com/rust-lang/rust/issues/51154
在任何盒子版本中添加 impl 关键字似乎无济于事。像我在未装箱的“工作”版本中使用它的语法似乎是新的,我还没有找到很好的文档。这里有一些关于它的信息:https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
更多相关链接:
How do I store a closure in Rust?
Closure in the return type for a Rust function
【问题讨论】:
-
也许您可以创建一个新类型
struct BoxedProbe(Box<Fn...>),然后为转发呼叫的新类型实现Fn()... -
我相信这是因为
Box<Fn(...)>本身并没有实现Fn(...)。 Issue #38132 是开放的...只是可能没有人费心做 PR。 -
@rodrigo 我是 rust 新手。我想如果我的新结构可以实现 add_probe 需要作为参数的确切 Fn 签名,那么我可以传递具有已知大小的类型吗?那么它的方法会被称为 add_probe 回调吗?我觉得这将是很多样板。
-
@trentcl 不完全相同的错误,但我觉得你可能是正确的。如果 Box of Fn did 实现了 Fn 本身,那么我可以将装箱的值传入吗?该问题的副本有一个更好的例子:github.com/rust-lang/rust/issues/47024
-
不知道
add_probe的定义,我只是猜测,但似乎确实如此。我希望,如果适当的impls 被添加到核心,那么事情将“正常工作”。