我正在尝试编写一个工厂函数来创建用于gstreamer中的“pad回调”的闭包。我提供了一个简化的示例,该示例应与the gstreamer crate和安装的gstreamer二进制文件/插件一起编译。通过我的研究,我已经通过使用“Impl trait trait”方法(而不是装箱)使工厂功能正常工作。不过,我想弄清楚装箱的方法,因为在某些情况下它似乎更合适。这和我所得到的差不多。通过取消注释标记为Closure function using 'Box<>'的部分可以看到该问题。我尝试将Fn部分指定为where clause的类型参数,以及许多其他尝试。在此尝试中,问题似乎在于,由于需要编译时大小,因此我无法解包闭包函数以用作对本地变量的分配或在add_probe回调中使用,这是首先放在盒子里...Ctrl + C或stdin中的'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(此选项不会关闭任何堆栈值,因此没有'move'。) Rust closures from factory functions(更多让我觉得应该拥有的东西……) rust 皮书的Box 部分: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 sizeerror[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`我知道需要基于堆栈的绑定(bind)来需要编译时的大小....因此,除非add_probe函数本身接受Boxed 争论,否则这几乎是不可能的。再进行一些尝试。包括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::Pass19 | | }) | |_________^ expected type parameter, found closure | = note: expected type `F` found type `[closure@src/main.rs:15:18: 19:10 x:_]`这似乎是因为我们已经指定了上面的类型,但是类(class)的结束是它自己的类型。尝试以下操作不起作用,因为它是一个特质,并且不能使用'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::Pass19 | | } 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::Pass19 | | } as F) | |______________^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait它提到了“发件人”特征。我没有对此进行研究,因为为闭包隐含特质似乎不正确。我什至不知道有可能吗?我还尝试了他们似乎称之为类型归属的方法(而不是使用':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 expected trait core::ops::FnMut, found type parameter https://doc.rust-lang.org/std/boxed/trait.FnBox.html (adsbygoogle = window.adsbygoogle || []).push({}); 最佳答案 自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 acollection 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>, whichdoes 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 possibleto call a Box<dyn FnOnce()>, so FnBox is in a sort of Limbo wherepeople don't want to fix or stabilize it to work around a temporaryissue.Adding an impl to core to make Fn() work may nevertheless conflict with FnBox in ways that I don't quite grasp. There areseveral comments about this on issue#28796.It's possible that implementing Fn() for Box<dyn Fn()> just can'tbe done in the language as is. It's also possible that it could bedone, but is a bad idea for forwards compatibility reasons; and it'salso possible that it could be done and is a good idea, but nobody hasdone it yet. That said, with things as they are now, you have a coupleof mostly unpleasant options.As someone suggested in the question comments, you could make your ownwrapper struct that wraps a closure, and implement Fn() forBox<Wrapper<F>>.You could make your own trait ProbeFn, which is implemented forany 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 aBox<dyn Fn()>. This works in the above example: call(&*h);Unlike Box<dyn Fn()>, &dyn Fn() does implement Fn(). It's notas general, though, because obviously it doesn't have ownership.However, it does work on the stable compiler -- implementing Fn()yourself requires unstable. (adsbygoogle = window.adsbygoogle || []).push({});
10-06 15:39