简体   繁体   English

如何在泛型返回值中引用闭包的类型?

[英]How to refer to the type of a closure in a generic return value?

I have a struct that contains a function, let's say of type F: Fn() -> String .我有一个包含函数的结构,比如说F: Fn() -> String And I want to initialize it in its constructor to a particular default function, which can later be overridden by the caller if they like.我想在它的构造函数中将它初始化为一个特定的默认函数,如果他们愿意,以后可以被调用者覆盖。

struct Builder<F> {
    func: F,
}

fn new_builder() -> ??? {
    let hello = "hello".to_owned();
    Builder { func: move || hello }
}

impl<F: Fn() -> String> Builder<F> {
    fn build(self) -> String {
        (self.func)()
    }
}

But how do I write the return type of new_builder() , since || {}但是我该如何写new_builder()的返回类型,因为|| {} || {} has an anonymous type? || {}有匿名类型? I tried with -> Builder<_> but that fails ( playground ), and suggests a solution that's not even syntactically valid:我尝试使用-> Builder<_>但失败了( playground ),并提出了一个甚至在语法上都无效的解决方案:

error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
 --> src/lib.rs:5:29
  |
5 | fn new_builder() -> Builder<_> {
  |                     --------^-
  |                     |       |
  |                     |       not allowed in type signatures
  |                     help: replace with the correct return type: `Builder<[closure@src/lib.rs:6:21: 6:26]>`

For more information about this error, try `rustc --explain E0121`.

I know I can solve it by making Builder non-generic with some boxing:我知道我可以通过使用一些拳击使Builder非泛型来解决它:

struct Builder {
    func: Box<dyn Fn() -> ()>,
}

But that incurs additional runtime overhead in the form of allocations and indirections, and might make it harder for the compiler to optimize, eg inline calls to func .但这会以分配和间接的形式产生额外的运行时开销,并且可能会使编译器更难优化,例如对func的内联调用。 This workaround is plenty fast in my practical situation, but I'm still wondering if Rust lets me stick to a purely static solution here.在我的实际情况下,这种解决方法非常快,但我仍然想知道 Rust 是否让我在这里坚持使用纯静态解决方案。

Is there a way to make this work without resorting to references or boxing?有没有办法在不求助于参考或拳击的情况下完成这项工作?

The main problem here is that the type of a closure cannot be written out in code .这里的主要问题是闭包的类型不能用代码写出来

Luckily, for exactly that reason, the impl keyword exists for return types:幸运的是,正是由于这个原因,返回类型存在impl关键字:

struct Builder<F> {
    func: F,
}

fn new_builder() -> Builder<impl FnOnce() -> String> {
    let hello = "hello".to_owned();
    Builder {
        func: move || hello,
    }
}

impl<F: FnOnce() -> String> Builder<F> {
    fn build(self) -> String {
        (self.func)()
    }
}

fn main() {
    let builder = new_builder();
    println!("{}", builder.build());
}

Which means: "This is some unspecified type that the compiler will figure out. It's just important that this type implements FnOnce() -> String ".这意味着:“这是编译器将找出的一些未指定的类型。这种类型实现FnOnce() -> String很重要”。

Other functions that use new_builder will then be able to access func as an FnOnce() -> String object.其他使用new_builder的函数将能够访问func作为FnOnce() -> String对象。

Also note that I had to change the type to FnOnce because func in your case consumes the hello and is therefore only callable once.另请注意,我必须将类型更改为FnOnce因为在您的情况下func会消耗hello ,因此只能调用一次。

Don't worry, though.不过不用担心。 impl<F: FnOnce() -> String> also includes Fn and FnMut , as they are also FnOnce implicitely. impl<F: FnOnce() -> String>还包括FnFnMut ,因为它们也是隐含的FnOnce

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM