如何在结构中存储生成器?

时间:2018-06-08 20:25:54

标签: types rust closures generator

我想做this

#![feature(nll)]
#![feature(generators, generator_trait)]
use std::ops::Generator;

struct Container<G: Generator<Yield = i32, Return = ()>> {
    generator: G
}

impl<G: Generator<Yield = i32, Return = ()>> Container<G> {
    pub fn new() -> Self {
        let q = 42;
        Container{ generator: || {
            yield 2i32 * q;
        } }
    }
}

fn main() {}

我收到此错误:

error[E0308]: mismatched types
  --> src/main.rs:12:31
   |
   |           Container{ generator: || {
   |  _______________________________^
   | |             yield 2i32 * q;
   | |         } }
   | |_________^ expected type parameter, found generator
   |
   = note: expected type `G`
              found type `[generator@src/main.rs:12:31: 14:10 q:_ _]`

感谢"Expected type parameter" error in the constructor of a generic struct我离我更近了,在impl之后删除了泛型类型(因为我没有为任意G实现结构。我尝试了这些变体,没有一个工作:

impl Container<G> 
where
    G: Generator<Yield = i32, Return = ()>
{ /* ... */ }
impl Container<Generator<Yield = i32, Return = ()>> { /* ... */ }
impl Container<_> { /* ... */ }

2 个答案:

答案 0 :(得分:1)

我想我找到了答案,但如果有人有更好的答案,请随意发帖。我显然对这方面知之甚少,或者我不会问。

为了完整性:需要泛型G,因为生成器就像闭包,每个闭包都有不同的类型(不仅仅是每个声明 - 每个实例化),因为它捕获了不同的环境。

正如"Expected type parameter" error in the constructor of a generic struct所指出的,第一个问题是impl之后的泛型类型。这意味着实现是针对外部选择的T,但返回的Self具有类型参数的特定值,即生成器的一个。

至于如何更换它,

  • impl Container<G> where G: Generator<Yield = i32, Return = ()>

    这不起作用,因为尽管G有限(非常严格),但仍然没有人负责选择特定的G

  • impl Container<_>

    这不起作用,因为类型推断不适用于struct实现。这可能是有意义的 - 除了'构造函数'之外,它对任何事情都不合逻辑。

  • impl Container<Generator<Yield = i32, Return = ()>>

    这不起作用,因为Generator是一个特征,特征对象的大小没有(而这个类型参数的大小应该是这样)。

可以解决尺寸问题。我花了一些尝试,但我不确定它是否完美,但在实现中添加了Box 修复了它。请注意,Box不属于Container;显然Box<Generator<...>>也满足约束G: Generator<...>

认为它也大大降低了生成器被移动的几率,我认为不应该发生:

  

函数resume不安全,因为它可以在不可移动的生成器上使用。在这样的调用之后,不可移动的生成器不能再次移动,但编译器不强制执行此操作。

完整的代码:

#![feature(nll)]
#![feature(generators, generator_trait)]

use std::ops::Generator;

struct Container<G>
where
    G: Generator<Yield = i32, Return = ()>,
{
    generator: G,
}

impl Container<Box<Generator<Yield = i32, Return = ()>>> {
    pub fn new() -> Self {
        let q = 42;
        Container {
            generator: Box::new(move || {
                yield 1i32 * q;
                yield 2i32 * q;
                yield 3i32 * q;
            }),
        }
    }
}

fn main() {}

游乐场for the aboveusage example

答案 1 :(得分:1)

一直不适用的技巧是将生成器(或迭代器,这些是非常相似的概念)的创建拆分为单独的函数。这允许您使用impl Trait

fn container_core(q: i32) -> impl Generator<Yield = i32, Return = ()> {
    move || {
        yield 1 * q;
        yield 2 * q;
        yield 3 * q;
    }
}

impl<G> Container<G>
where
    G: Generator<Yield = i32, Return = ()>,
{
    pub fn new(generator: G) -> Self {
        Container { generator }
    }
}

fn main() {
    let x = Container::new(container_core(42));
}

您仍然无法命名x的类型,因此您无法将其存储在结构中,因此根本问题尚未解决。当然,您可以结合答案:

impl Container<Box<Generator<Yield = i32, Return = ()>>> {
    fn new_boxed(q: i32) -> Self {
        Container::new(Box::new(container_core(q)))
    }
}