我正在尝试编写一个工厂函数来创建闭包,用作gstreamer中的“pad callbacks”。我提供了一个精简的示例,应该使用the gstreamer crate和gstreamer二进制文件/插件进行编译。
通过我的研究,我通过使用'impl trait'方法而不是装箱来获得工厂功能。我想弄清楚盒装方法,因为它在某些情况下似乎更合适。
这就像我得到的那样接近。通过取消注释标记为Closure function using 'Box<>'
的部分可以看出问题。我已尝试将Fn
部分指定为带有where clause
的类型参数,以及许多其他尝试。在这种尝试中,看起来问题是我不能将闭包函数取消装箱以用作局部变量的赋值,或者由于需要编译时大小而在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,但我似乎无法想象如何将任何解决方案应用于这个特定的功能。我在标题中加入了“非平凡”和“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(更让我觉得我应该做的更多......)
盒及LT;&GT;生锈书的一部分: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&lt;&gt;,否则这几乎感觉不可能。 arguement?
进行更多尝试。一些地方,包括add_probe函数签名本身使用Type参数和'where'子句来指定Fn特征。
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
它提到了'从'特性。我没有调查过,因为关闭闭包的特征似乎并不合适。我甚至不确定它是否可能?
我也尝试了他们似乎称之为类型归属的东西(而不是使用':F'代表'作为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
答案 0 :(得分:2)
这个问题可以简化为一个非常简洁的例子。 (截至本文撰写时,稳定的Rust为1.26。在这篇文章中,我使用的是beta 1.27,所以我可以利用dyn Trait
syntax - 我认为这会让事情更加清晰。)
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
}
问题的核心是Box<dyn Fn()>
没有实现Fn()
。没有良好的原因这不起作用,但有一系列因素使其难以修复:
self
获取的方法。这使得无法调用Box<dyn FnOnce()>
。目前的解决方法是使用Box<dyn FnBox>
, 实施FnOnce()
(但这并不直接适用于您的情况或上面的示例,因为您要使用{{1 }})。Fn
,因此Box<dyn FnOnce()>
处于一种Limbo,人们不想修复或稳定它以解决临时问题。 FnBox
添加到核心以使impl
工作可能会与Fn()
以我无法理解的方式发生冲突。 There are several comments about this on issue #28796。对于FnBox
实施Fn()
可能无法按语言完成。它也有可能完成,但出于兼容性的原因,这是一个坏主意;也有可能它可以做到并且是一个好主意,但还没有人做过。也就是说,就像现在的情况一样,你有几个最不愉快的选择。
正如有人在问题评论中建议的那样,你可以创建自己的包装结构来封装一个闭包,并为Box<dyn Fn()>
实现Fn()
。
您可以创建自己的特征 Box<Wrapper<F>>
,这是针对正确类型的任何关闭实现的,并为ProbeFn
实现Fn()
。
在某些情况下,您可以使用Box<dyn ProbeFn>
代替&dyn Fn()
。这适用于上面的例子:
Box<dyn Fn()>
与 call(&*h);
不同,Box<dyn Fn()>
实施&dyn Fn()
。但这并不是一般的,因为显然它没有所有权。但是,它确实适用于稳定的编译器 - 自己实现Fn()
需要不稳定。